React前端导出XLSX至Express后端并邮件发送的问题求助
问题:大Excel文件邮件发送报413错误,改用FormData传输后附件空白
我用xlsx库把对象数组转成Excel表格,原本用nodemailer发邮件没问题,但数组超过75个对象时,base64编码后的请求Payload太大,返回413错误。改成用FormData+axios把.xlsx文件传到后端,用multer接收,但邮件附件变成空白表格,求排查问题。
前端代码
const exportDataToSheet = async (email: string, dataArray: any) => { try { const listProducts = []; for (const object of dataArray.objects) { const dataObject = await getData(data.nr_pedido, data.cd_filial); listProducts.push(dataObject) } const workBook = XLSX.utils.book_new(); const excelHeaders: string[] = [ "Product", "Network", "Branch", "Branch name", ]; const productData = []; for (const product of listProducts) { const productDetails = [ `${product?.data[0]?.nr_product ?? "-"}`, `${product?.data[0]?.id_branch ?? "-"}`, //...a lot of more info... ] productData.push(productDetails) } productData.unshift(excelHeaders); const workSheet = XLSX.utils.aoa_to_sheet(productData); XLSX.utils.book_append_sheet(workBook, workSheet, "Infos on the products"); const file = XLSX.writeFile(workBook, {bookType:'xlsx'}); const formData = new FormData(); formData.append('file', file); axios.post('http://localhost:3001/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }).then(response => { console.log('File successfully sent.'); }) .catch(error => { console.error(error); }) await requestMailer({ email: email, model: "exportProductData", filename: `data_products.xlsx`, }); } catch (error) { showAlert(error.response ? error.response.data.message : error.message, "error"); } }
后端代码
export default class EmailNodeMailerService { static async sendEmail(req: Request) { const express = require('express'); const multer = require('multer'); const upload = multer({dest: './uploads'}); const app = express(); let file; app.post('/upload', upload.single('arquivo'), (req, res) => { file = req.file; console.log('File received: ', file.originalName); res.send('File successfully sent.') }) //...various lines regarding node mailer... mailOptions.attachments = [ { filename: filename ? filename : 'product_data.xlsx', content: file, contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } ]; } //...more code regarding node mailer... } catch (error) { throw new Error(error); } } }
问题排查与修复方案
前端核心问题
- XLSX.writeFile使用错误:
XLSX.writeFile是直接写入本地文件的方法,返回值为undefined,你把它赋值给file等于传了空值给FormData。应该用XLSX.write生成Buffer,再转成Blob对象。 - FormData文件格式错误:需要把Excel Buffer包装成Blob并指定正确的MIME类型,再添加到FormData。
- 请求时序错误:你在发送FormData后立刻调用
requestMailer,此时后端还没接收到文件,邮件发送时file变量为空。必须等待文件上传完成再触发邮件。
修复后的前端关键代码:
// 替换原XLSX.writeFile部分 const excelBuffer = XLSX.write(workBook, { bookType: 'xlsx', type: 'buffer' }); const blob = new Blob([excelBuffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const formData = new FormData(); formData.append('file', blob, 'data_products.xlsx'); // 第三个参数指定文件名 // 等待文件上传完成再发邮件 await axios.post('http://localhost:3001/upload', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); await requestMailer({ email: email, model: "exportProductData", filename: `data_products.xlsx`, });
后端核心问题
- Multer字段名不匹配:前端FormData的字段是
file,但后端用upload.single('arquivo'),字段名不一致导致req.file为undefined。 - 邮件附件内容错误:multer返回的
req.file是文件信息对象,不是文件内容。需要读取文件二进制数据作为附件content。 - Express实例重复创建:在
sendEmail方法内每次创建新的Express实例和路由是错误的,应该把路由定义在服务启动时,而非邮件方法内部。
修复后的后端关键代码:
// 路由配置放在服务启动文件中(不是sendEmail方法内) const express = require('express'); const multer = require('multer'); const fs = require('fs').promises; const app = express(); const upload = multer({ dest: './uploads' }); // 临时存储上传的文件信息(生产环境建议用数据库或唯一标识) let uploadedFileInfo; app.post('/upload', upload.single('file'), async (req, res) => { uploadedFileInfo = req.file; console.log('File received: ', uploadedFileInfo.originalname); res.send('File successfully sent.'); }); // 邮件服务类 export default class EmailNodeMailerService { static async sendEmail(req) { // ...其他nodemailer配置... if (uploadedFileInfo) { // 读取上传的文件内容 const fileContent = await fs.readFile(uploadedFileInfo.path); mailOptions.attachments = [ { filename: req.body.filename || 'product_data.xlsx', content: fileContent, contentType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } ]; } // ...发送邮件的代码... } }
额外优化建议
- 避免用全局变量存储文件信息,可在上传时返回文件ID,邮件请求携带ID读取文件。
- 定时清理上传的临时文件,避免磁盘占用过高。
- 考虑把Excel生成逻辑移到后端,前端只传原始数据,减少前端计算压力和传输量。
内容的提问来源于stack exchange,提问作者gustop01
相关产品推荐
相关产品推荐

