node文件下载主要有两种方式,第一种就是原生的设置请求头的content-type 与 Content-Disposition来告诉浏览器,响应结果属于文件下载,准备好接收文件;第二种是使用express框架自带的下载api。
1. 设置响应头的方式
这里需要补充一下,http文件下载的知识。文件下载其实就是通过设置响应头的响应内容和行为,然后将数据流放入到响应体中,浏览器读取响应头的时候,得知这是什么类型的文件数据,浏览器会整理并打开自己的保存下载文件机制。
这里贴上各种文件的传输content-type查询链接:https://tool.oschina.net/commons
案例展示: 我需要下载test.txt文件
-
首先在route中提供文件下载的api
app.route('/download').get(TestController.download);
-
在TestController中实现download方法,特别要注意响应头的content-type的设置,不同的文件类型会是不一样的content-type,可以根据上面的链接进行查询。
const download = async(req, res) => { // 第一步,先获取文件的buffer字节流,这里具体做了什么,看第三步 let {fileName, buffer} = await TestService.download(); res.setHeader('Content-Type', 'application/octet-stream'); res.setHeader('Content-Disposition', `attachment;filename=${fileName}`); res.status(200).send(buffer); }
-
实现TestService的download方法
const download = () => { let filePath = '/test.txt'; let buffer = fs.readFileSync(filePath); let fileName = path.basename(filePath); return {fileName, buffer}; }
-
到这里,后端文件下载部分就完成了,如果你是通过超链接,点击一下就下载的话,可以顺利地去触发浏览器的默认下载行为,你的浏览器应该就会提示你正在下载了。如果你不是通过a标签,而是通过ajax或者axios的话,这时候需要前端再去通过超链接或者ifram去访问后端,触发浏览器的默认下载行为。
若有需要,可以参考这篇文章:https://blog.csdn.net/interestANd/article/details/121855311
2. 通过express框架的download api去实现文件下载
用这个的话,相对第一种方式就省事很多,他会帮我们把文件读取转换成二进制流,而且不需要我们设置响应头等信息,也能实现同样的效果。
res.download(path, [filename], [fn])
path
所需传输附件的路径, 通常情况下浏览器会弹出一个下载文件的窗口。 浏览器弹出框里的文件名和响应头里的Disposition “filename=” 参数是一致的, 你也可以通过传入filename
来自由设置。当在传输的过程中发生一个错误时,可选的回调函数
fn
会被调用执行。 这个方法使用res.sendfile()传输文件。res.sendfile(path, [options], [fn]])
path
所传输附件的路径。它会根据文件的扩展名自动设置响应头里的Content-Type字段。 回调函数
fn(err)
在传输完成或者发生错误时会被调用执行。Options:
maxAge
毫秒,默认为0root
文件相对的路径这个方法可以非常良好的支持有缩略图的文件服务。
参考代码:
主要区别是在于controller层,其余的代码一致,不过由于我的这个案例需求实在是太简单了,就省略service层不做了。
const download = async (req, res) => {
try {
let filePath = '/test.txt';
let fileName = path.basename(filePath);
res.download(filePath, fileName);
} catch (error) {
return res.status(500).send({
result: 'error',
message: `Failed to download file: ${error.message}`
})
}
}