Node + axios +koa 实现excel文件流导出

2,742 阅读2分钟

1.后端导出文件流

1.1 后端用node-xlsx导出excel文件

const xlsx = require("node-xlsx")
// data为需要导出的数据,格式为[{name:"张三",age:18},{name:"李四",age:20}]
// options为设置导出excel文件的样式,如需要设置查询node-xlsx
module.exports = (data, options={}) =>{
  let xlsxObj = [
    {
      name: "sheet",
      data: []
    }
  ]
  data.forEach((item,idx) => {
    if(idx === 0){
    	// 第一行为表头
      xlsxObj[0].data.push(Object.keys(item))
    }
    // 其余行为excel数据
    xlsxObj[0].data.push(Object.values(item))
  });
  // 返回一个buffer对象
  return xlsx.build(xlsxObj, options)
}

1.2 在koa中调用导出方法

// require的路径需要改成你本地的路径
const exportExcel = require("../../utils/exportExcel")
// 使用sequelize拿到数据库数据
const res = await models.Company.findAll(query)
// 将数据转换为Buffer
let buffer = exportExcel(res)
// 设置content-type请求头
ctx.set('Content-Type', 'application/vnd.openxmlformats');
// 设置文件名信息请求头
ctx.set('Content-Disposition', "attachment; filename=" + encodeURIComponent("文件名") + ".xlsx");
// 文件名信息由后端返回时必须设置该请求头,否则前端拿不到Content-Disposition响应头信息
ctx.set("Access-Control-Expose-Headers", "Content-Disposition")
// 将buffer返回给前端
ctx.body = buffer

2.在前端axios调用

//使用axios.post调用,user.post封装了一下,实质就是axios
 const { data, headers } = await user.post({
  url,
  data: this.params,
  // responseType设置为arraybuffer,注意,一定要和data同级,否则下载文件之后会出现文件已损坏
  responseType: "arraybuffer"
})
// 使用Blob处理文件流
const blob = new Blob([data])
// 获取导出文件名,decodeURIComponent为中文解码方法
const fileName = decodeURIComponent(headers["content-disposition"].split("filename=")[1])
// 通过a标签进行下载
let downloadElement = document.createElement('a');
let href = window.URL.createObjectURL(blob);
downloadElement.href = href;
downloadElement.download = fileName;
document.body.appendChild(downloadElement);
downloadElement.click();
document.body.removeChild(downloadElement);
window.URL.revokeObjectURL(href);

3. 导出的时候需要注意的几个点

3.1 如果导出文件名由后端给出,并且含有中文,中文需要使用encodeURIComponent转码,那么需要设置Content-Disposition请求头,并且必须设置Access-Control-Expose-Headers将Content-Disposition暴露出来,否则前端拿不到Content-Disposition请求头

ctx.set('Content-Disposition', "attachment; filename=" + encodeURIComponent("文件名") + ".xlsx");
ctx.set("Access-Control-Expose-Headers", "Content-Disposition")

3.2 前端请求时必须设置responseType: "arraybuffer",且必须和data同级,否则下载的文件会提示文件已损坏

 const { data, headers } = await user.post({
      url,
      data: this.params,
      // 同级 同级 同级 重要的事情说三遍
      responseType: "arraybuffer"
    })