思路
1. 服务端生成文件,将文件在服务端的地址返回给前端供前端下载。
2. 服务端直接通过文件流传递给前端,前端直接将导出的接口地址使用window.open打开。(单纯的导出功能)
3. 服务端将文件流传给前端,前端将文件流转为blob对象并下载。
4. 服务端将文件流存在公共变量中,post请求上传文件后,再根据文件导出来进行请求。(只局限于每次上传并导入操作时,只有一个用户)
实战
写这篇帖子,主要是因为想做一个服务端接收前端待导入的excel,并判断excel中的错误数据,将错误数据收集后以excel的形式导出给前端。
在做这个内容的时候,由于知识面的狭窄走了很多弯路,特此写一篇博客来纪念一下。
服务端语言:node.js
前端框架:vue2
前端代码:前端方便比较简单,利用formData将文件信息上传给服务端,不细说了
服务端核心代码:
// service层
const upload = async ctx => {
// 文件导出操作
//涉及模块:
// 1. formidable 读取上传的文件信息
// 2. fs 将文件暂存至本地
// 3. xlsx 解析excel文件,转换为数组
// 4. 导入数据并将错误数据收集
//若存在错误数据
if(wrongDataList){
// 导出操作
ctx.body = export(wrongDataList);
}
else{
ctx.body = "导入成功";
}
}
function export(wrongDataList){
//涉及stream模块
wrongDataList.unshift(rowNameArray);// rowNameArray: 由列名组成的数组
let buffer = xlsx.build(wrongDataList);// 获得buffer对象
let passThroughStream = new stream.PassThrough();// 定义一个双向流
passThroughStream.write(buffer);// 将buffer数据写入双向流
return passThroughStream;
}
// controll层
const importFile = async ctx=>{
await upload(ctx);
}
这样写完后,前端通过post请求上传文件,并在回调函数中接收到了stream流,但是不能自动下载。我就很疑惑了,为什么之前接触过的单独导出文件接口可以直接通过get请求方式,浏览器自动将文件流解析成文件并下载呢?
结论
经过一系列资料查询后并得出以下结论
1. 无论下载什么链接,异步ajax都无法直接下载,必须通过a标签或打开新窗口进行下载
(PS:经过JS跳板群兄弟指导得出)
(1) a标签设置download属性,可以直接下载普通链接能够访问的文件
(2) 新窗口只能下载,响应头为Content-Type:application/octet-stream的文件
2. 后端将文件流返回给前端的时候,要设置请求头,并且文件名字不能使用中文。
"Content-Disposition","attachment;filename=文件名字+文件类型"
3.在返回文件流的时候一定要在请求头带上文件流的字节长度(Content-Length)!!切记!切记!切记!
否则,前端要么下载文件失败,要么下载下来的文件无法打开。