ExcelJS服务端编辑Excel文件后返回前端本地下载方案
服务端生成Excel后直接下载到本地改造方案
核心实现逻辑:不再仅将生成的Excel写入服务端磁盘,而是通过ExcelJS直接生成二进制文件流,接口设置下载响应头返回流内容,前端接收二进制Blob后触发浏览器原生下载,可根据需求选择是否保留服务端文件留存逻辑。
一、后端代码改造
1. 调整Excel生成函数
将原回调风格的函数改为async/await异步写法,新增Buffer返回逻辑,原有单元格赋值逻辑无需修改:
async function hojaDeVida(BD, carpeta, sheetName, tipoDeDocumento, documento, nombreCompleto, direccion, telefono, celular, correoEmpleado, genero, fechaDeNacimiento, estadoCivil, numeroDeHijos, fechaDeIngreso, fechaDeRetiro, cargo, observacion, centroDeCostos, turno, tipoDeContrato, salario, nombreDeBanco, tipoDeCuenta, numeroDeCuenta, tallaDelantal, tallaSudadera, tallaJeans, tallaZapatos, cajaDeCompensacion, fondoDeCesantias, fondoDePensiones, eps, arl, nivelDeRiesgo) { const wb = new ExcelJS.Workbook(); await wb.xlsx.readFile(BD); const ws = wb.getWorksheet(sheetName); // 单元格赋值逻辑与原代码完全一致 ws.getCell("C3").value = tipoDeDocumento; ws.getCell("C4").value = documento; ws.getCell("C5").value = nombreCompleto; ws.getCell("C6").value = direccion; ws.getCell("C7").value = telefono; ws.getCell("C8").value = celular; ws.getCell("C9").value = correoEmpleado; ws.getCell("C10").value = genero; ws.getCell("C11").value = fechaDeNacimiento; ws.getCell("C12").value = estadoCivil; ws.getCell("C13").value = numeroDeHijos; ws.getCell("C15").value = fechaDeIngreso; ws.getCell("C16").value = fechaDeRetiro; ws.getCell("C17").value = cargo; ws.getCell("C18").value = observacion; ws.getCell("C19").value = centroDeCostos; ws.getCell("C20").value = turno; ws.getCell("C21").value = tipoDeContrato; ws.getCell("C22").value = salario; ws.getCell("C24").value = nombreDeBanco; ws.getCell("C25").value = tipoDeCuenta; ws.getCell("C26").value = numeroDeCuenta; ws.getCell("C28").value = tallaDelantal; ws.getCell("C29").value = tallaSudadera; ws.getCell("C30").value = tallaJeans; ws.getCell("C31").value = tallaZapatos; ws.getCell("C33").value = cajaDeCompensacion; ws.getCell("C34").value = fondoDeCesantias; ws.getCell("C35").value = fondoDePensiones; ws.getCell("C36").value = eps; ws.getCell("C37").value = arl; ws.getCell("C38").value = nivelDeRiesgo; // 需保留服务端文件存储则保留此行,不需要可删除 await wb.xlsx.writeFile(`${carpeta}/13) Hoja de vida.xlsx`); // 生成二进制流用于接口返回 const fileBuffer = await wb.xlsx.writeBuffer(); return fileBuffer; };
2. 调整接口响应逻辑
以Express框架为例,接口设置下载专用响应头,直接返回生成的文件Buffer:
app.post('/documentos', async (req, res) => { try { // 从请求体中解析hojaDeVida需要的入参,按实际传参结构取值即可 const params = req.body; const fileBuffer = await hojaDeVida(/* 传入所有对应参数 */); // 设置响应头,告知浏览器当前响应为文件下载 res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'); res.setHeader('Content-Disposition', 'attachment; filename="Hoja_de_vida.xlsx"'); res.send(fileBuffer); } catch (err) { res.status(500).json({ code: 500, msg: '文件生成失败' }); } })
二、前端代码改造
Axios请求新增responseType: 'blob'配置,告知客户端将响应解析为二进制对象,收到响应后创建临时DOM节点触发下载:
await axios.post(`${HOST}/documentos`, data, { responseType: 'blob' // 必须添加,否则会出现文件乱码、损坏 }) .then((response) => { if (response.status === 200) { // 处理文件下载 const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }); const downloadUrl = window.URL.createObjectURL(blob); const aTag = document.createElement('a'); aTag.href = downloadUrl; // 自定义本地保存的文件名 aTag.download = `Hoja_de_vida_${Date.now()}.xlsx`; document.body.appendChild(aTag); aTag.click(); // 清理临时资源,避免内存泄漏 window.URL.revokeObjectURL(downloadUrl); document.body.removeChild(aTag); // 原有数据库更新、弹窗逻辑保留 update(ref(getDatabase(), `BD_PASTEUR/${record.ID}`), { DOCUMENTOS: "DOCUMENTOS GENERADOS", }).then(() => { Swal.fire({ title: 'Documentos generados', text: 'Los documentos se han generado correctamente', icon: 'success' }); }); }; }).catch((error) => { Swal.fire({ title: 'Oops', text: 'Ha ocurrido un error, contacta al administrador', icon: 'error' }) });
三、注意事项
- 若使用Koa框架,仅需将响应头设置语法调整为
ctx.set(),将ctx.body赋值为fileBuffer即可,核心逻辑无变化 - 所有Excel读写的异步操作必须添加await,否则会出现文件未生成完成就返回,导致下载的文件损坏、为空
- 若下载后文件打开报错,优先检查接口是否存在全局统一JSON响应包装,确保该接口直接返回二进制流,不额外包裹code、msg等JSON字段
- 可根据业务需求动态拼接下载文件名,比如拼接员工姓名、文档编号等信息,优化用户体验
内容的提问来源于stack exchange,提问作者Don-Mandi
相关产品推荐
相关产品推荐

