本地可生成下载PDF/Excel,生产环境无法触发客户端下载的技术求助
问题描述
我尝试创建包含订单详情的PDF和Excel文件,本地触发函数时文件会下载到本地目录,但生产环境(Web端)触发时无法完成下载。请问如何让函数在Web端将文件下载到客户端电脑?问题出在哪个环节?
问题根源
你的代码存在核心逻辑错误:
doc.save()(jsPDF)和XLSX.writeFile()(SheetJS)的作用是在服务器本地磁盘生成文件,而非将文件内容发送给客户端浏览器。- 本地开发时,服务器与客户端是同一台机器,所以你能在本地看到生成的文件;但生产环境中,文件只会保存在服务器磁盘上,客户端浏览器完全无法访问这些文件,且后续的
res.redirect会直接中断响应,客户端根本没机会接收文件数据。
修复方案
需要将生成的文件转为Buffer/二进制数据,通过HTTP响应头告知浏览器这是待下载文件,再将数据直接发送给客户端,而非在服务器本地保存。
修复后的PDF生成代码
module.exports.createPDF = async (req, res, next) => { try { const id = req.body.id; const doc = new jsPDF({ orientation: 'p', unit: 'mm', format: 'a4' }); let txtFormat = ''; for (let id_num of id) { const order = await Order.findById(id_num).populate('customer'); txtFormat += `Articulo: ${order.name} -- Precio: ${order.price} -- Nombre: ${order.customer.username}\nAsiento: ${order.seat}${order.letter} -- Seccion: ${order.section}\n*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*\n`; } doc.text(txtFormat, 20, 20); // 生成唯一文件名 const date = new Date(); const month = date.getMonth() + 1; const day = date.getDate(); const year = date.getFullYear(); const file_num = Math.floor(1000 + Math.random() * 9000); const fileName = `${day}_${month}_${year}--${file_num}.pdf`; // 获取PDF的Buffer数据 const pdfBuffer = doc.output('arraybuffer'); // 设置响应头,告知浏览器触发下载 res.setHeader('Content-Type', 'application/pdf'); res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`); // 将文件数据发送给客户端 res.send(Buffer.from(pdfBuffer)); } catch (e) { req.flash('Refresca la Pagina e Intenta de Nuevo'); res.redirect('/places'); } };
修复后的Excel生成代码
module.exports.createReport = async (req, res) => { try { const workBook = XLSX.utils.book_new(); const id = req.body.id; const all_orders = []; for (let id_num of id) { const order = await Order.findById(id_num); const new_order = { price: order.price, articulo: order.name, date: order.date, section: order.section }; all_orders.push(new_order); order.is_reported = false; await order.save(); // 加await确保数据库操作完成后再响应 } const workSheet = XLSX.utils.json_to_sheet(all_orders); XLSX.utils.book_append_sheet(workBook, workSheet, "orders"); // 生成唯一文件名 const date = new Date(); const month = date.getMonth() + 1; const day = date.getDate(); const year = date.getFullYear(); const file_num = Math.floor(1000 + Math.random() * 9000); const fileName = `${day}_${month}_${year}--${file_num}.xlsx`; // 生成Excel的Buffer数据 const excelBuffer = XLSX.write(workBook, { bookType: 'xlsx', type: "buffer" }); // 设置响应头 res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`); // 发送文件数据给客户端 res.send(excelBuffer); } catch (e) { req.flash('Refresca la Pagina e Intenta de Nuevo'); res.redirect('/places'); } };
额外优化点
- 移除了无用的
recipt_name变量,以及Excel代码中重复的XLSX.write调用。 - 为Excel代码中的
order.save()添加await,确保数据库操作完成后再返回响应。 - 不再使用
res.redirect,因为文件响应与重定向不能同时存在,客户端会优先处理文件下载。
内容的提问来源于stack exchange,提问作者nanovh
相关产品推荐
相关产品推荐

