angularnodemailerjs-xlsx

How to send excel file with nodemailer


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: 'gestionepresenze@gmail.com',
        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);
}

Solution

  • 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: 'gestionepresenze@gmail.com',
            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);
            }
        })
    })