I'm trying to send an email with an excel file attached, but when I open it an error pops up saying that the file is damaged. It also downloads the file (which I actually don't want to) and I can open that with no problem.
I'm using the js-xlsx
library to create the file and the nodemailer
library to send it.
Function in email.service.ts
:
sendEmail(values: any) {
let message: {from: string, to: string, subject: string, html: string, attachments: {}[] | null} = {
from: '[email protected]',
to: values.email,
subject: values.oggetto,
html: this.storage.createTable(values.messaggio).outerHTML,
attachments: null
}
if (values.attach) {
message["attachments"] = [{
filename: values.fileName '.xlsx',
content: this.excel.createExcel(values),
contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
}]
}
return this.http.post('http://localhost:3000/send', message)
}
Function in excel.service.ts
:
createExcel(values: any) {
const fileName = values.fileName '.xlsx';
const table = this.storage.createTable('');
const ws: XLSX.WorkSheet = XLSX.utils.table_to_sheet(table);
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Presenze');
return XLSX.writeFile(wb, fileName);
}
CodePudding user response:
From the documentation (https://docs.sheetjs.com/docs/api/write-options) I can see the following:
XLSX.writeFile(wb, filename, write_opts) attempts to write wb to filename. In browser-based environments, it will attempt to force a client-side download.
This is the reason a download is triggered on your client.
You probably want to use XLSX.write(workbook, { type: "array", bookType: "xlsx" });
, so that an Uint8Array
is returned from the function and attached as content to your form data.
CodePudding user response:
I got it to work.
First of all the right method is XLSX.write(wb, {type: 'buffer', bookType: 'xlsx'});
, not XLSX.writeFile(...)
.
But the server was still throwing an error, saying that I was passing an Object
. The problem was that I was sending what I thought was an Uint8Array
, while the node app was actually receiving an Object
.
Why? I don't know.
But knowing this I now convert the content to a Uint8Array
directly in the node app.
Here's how I changed the code:
Function in email.service.ts
:
sendEmail(values: any) {
let message: {from: string, to: string, subject: string, html: string, attachments: {}[] | null} = {
from: '[email protected]',
to: values.email,
subject: values.oggetto,
html: this.storage.createTable(values.messaggio).outerHTML,
attachments: null
}
if (values.attach) {
message["attachments"] = [{
filename: values.fileName '.xlsx',
content: this.excel.createExcel(values)
}]
}
return this.http.post('http://localhost:3000/send', message)
}
Function in excel.service.ts
:
createExcel(values: any) {
const fileName = values.fileName '.xlsx';
const table = this.storage.createTable('');
const ws: XLSX.WorkSheet = XLSX.utils.table_to_sheet(table);
const wb: XLSX.WorkBook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(wb, ws, 'Presenze');
return XLSX.write(wb, {type: 'buffer', bookType: 'xlsx'});
}
Function in the node app to send the email:
fastify.post('/send', (request, reply) => {
let content = request.body.attachments[0].content;
let array = new Uint8Array(Object.entries(content).length);
for (let [key, value] of Object.entries(content)) {
array[key] = value;
}
request.body.attachments[0].content = array;
const mailOptions = request.body
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
console.log(error);
} else {
console.log('Sent: ' info.response);
}
})
})