mysql查询结果转excel应采用流式分页查询+node-xlsx流式写入,设置content-type、content-disposition和content-length响应头,前端axios配responsetype: 'blob',避免oom和乱码。

MySQL查询结果怎么转成Excel文件?
直接用 node-xlsx 或 xlsx 库最稳。别碰 excel-export 这类老库,不维护、不支持 Buffer 流式写入,大数据量导出会 OOM。核心思路是:Node 层查 MySQL → 转成二维数组或对象数组 → 用 xlsx 生成工作表 → 设置响应头触发浏览器下载。
-
xlsx 支持 SheetJS 全特性,能处理日期、数字格式、合并单元格(虽然报表导出一般不需要)
- 查询必须加
rowsAsArray: true(mysql2)或手动映射字段名,否则对象键顺序不确定,Excel 列会错乱
- 千万别在 Express 的
res.send() 里直接传 Buffer,要用 res.end(buffer, 'binary') 或 res.send(Buffer.from(...)),否则中文变乱码
Express 接口怎么设响应头才让浏览器弹保存框?
关键就三行响应头,少一个都可能失败:
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
-
Content-Disposition: attachment; filename="report.xlsx"(注意 filename 不要带路径、不要空格、最好用英文+下划线)
-
Content-Length: ${buffer.length}(可选但建议加上,避免 Safari 下载卡住)
xlsx 支持 SheetJS 全特性,能处理日期、数字格式、合并单元格(虽然报表导出一般不需要)rowsAsArray: true(mysql2)或手动映射字段名,否则对象键顺序不确定,Excel 列会错乱res.send() 里直接传 Buffer,要用 res.end(buffer, 'binary') 或 res.send(Buffer.from(...)),否则中文变乱码Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet-
Content-Disposition: attachment; filename="report.xlsx"(注意 filename 不要带路径、不要空格、最好用英文+下划线) -
Content-Length: ${buffer.length}(可选但建议加上,避免 Safari 下载卡住)
如果前端用 axios 请求,记得配 responseType: 'blob';用 fetch 就得手动调 res.arrayBuffer() 再转 blob。否则返回的是一串乱码字符串,不是文件。
大表导出卡死或内存爆掉怎么办?
MySQL 查 10 万行以上数据时,connection.query() 默认把全部结果加载进内存,Node 进程很容易被系统 kill。必须改成分页流式读取:
- 用
connection.query('SELECT ...').on('result', callback)(mysql2 支持)
- 每次拿到一批行(比如 1000 条),push 到临时数组,达到阈值就调一次
worksheet.addRows()
- 最后一次性
workbook.writeBuffer() 输出,别每条都 write
- 更稳妥的做法是加个定时器,每 5 秒
res.write() 一点心跳数据,防 Nginx / 负载均衡超时断连
Vue 端点击导出按钮后没反应?检查这几点
常见静默失败原因:
- 后端接口返回 200 但内容是 JSON(比如 { code: 500, msg: 'db error' }),而前端没判断 status,直接当 Excel 处理 → 结果下载一个打不开的 .xlsx 文件
- 前端用了
router-link 或普通 <a></a> 标签,但没加 download 属性,且后端没设 Content-Disposition → 浏览器直接渲染二进制内容,页面变白屏
- 导出请求走了 Vue Router 的 history 模式,路径被重写,实际请求 404 → 打开浏览器 Network 面板看真实请求 URL 和状态码
- 用户点了多次,后端没做防重复(比如用 Redis 锁住同一用户 60s 内只能导一次),导致连接堆积、MySQL 连接数耗尽
connection.query('SELECT ...').on('result', callback)(mysql2 支持)worksheet.addRows()
workbook.writeBuffer() 输出,别每条都 writeres.write() 一点心跳数据,防 Nginx / 负载均衡超时断连- 后端接口返回 200 但内容是 JSON(比如 { code: 500, msg: 'db error' }),而前端没判断 status,直接当 Excel 处理 → 结果下载一个打不开的 .xlsx 文件
- 前端用了
router-link或普通<a></a>标签,但没加download属性,且后端没设Content-Disposition→ 浏览器直接渲染二进制内容,页面变白屏 - 导出请求走了 Vue Router 的 history 模式,路径被重写,实际请求 404 → 打开浏览器 Network 面板看真实请求 URL 和状态码
- 用户点了多次,后端没做防重复(比如用 Redis 锁住同一用户 60s 内只能导一次),导致连接堆积、MySQL 连接数耗尽
导出功能看着简单,真正上线后最常出问题的是边界:空数据怎么显示表头、时间字段要不要格式化为 YYYY-MM-DD、导出中途数据库连接断了怎么通知用户——这些都得在代码里显式处理,不能依赖库默认行为。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











