You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地可生成下载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');
  }
};
额外优化点
  1. 移除了无用的recipt_name变量,以及Excel代码中重复的XLSX.write调用。
  2. 为Excel代码中的order.save()添加await,确保数据库操作完成后再返回响应。
  3. 不再使用res.redirect,因为文件响应与重定向不能同时存在,客户端会优先处理文件下载。

内容的提问来源于stack exchange,提问作者nanovh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 05:20:25