前言
本文介绍如何在Nest实现文件下载以及科普下浏览器下文件打开与文件下载的区别。
实现原理
我的nest项目是默认的基于express实现。
问题等价于express如何实现一个下载接口?
- 读取文件,提取文件流
- 将文件流pipe置入响应
const writeStream = fs.createReadStream(filePath)
stream.pipe(response)
....略去一系列setHeader操作
这么麻烦?其实express有两个API:res.sendFile和res.download直接封装好上述的操作
回到主题,在nest里应该如何实现呢下载接口?
直接上代码
import { ClassSerializerInterceptor, Controller, Get, Logger, Query, Res, UseGuards, UseInterceptors } from '@nestjs/common';
import { Response } from 'express';
......
@Get('download')
async download(@Res() response: Response): Promise<any> {
......
response.download(filePath, (err) => {
if (!err) {
this.logger.log('success', 'download');
return;
}
this.logger.error(err.stack);
response.send(ApiResult.Error_Filter(500, '下载失败', err));
})
}
这里要注意的点是:Response必须从Express导入的,我们使用的是express底层的能力;并且nest的Respoonse是装饰器。
sendFile VS donwload
两个API有什么区别? 二者都是读取文件流并且以流响应,但download才是真正的下载。
如响应一个txt文本的时候,如果使用sendFile,浏览器会直接显示这段文本。但使用download是可以弹出“另存为”提示保存。
sendFile文档描述
Transfers the file at the given path.
Sets the Content-Type response HTTP header field based on the filename’s extension.
Unless the root option is set in the options object, path must be an absolute path to the file.
在给定的位置传输文件path。Content-Type根据文件名的扩展名设置响应HTTP标头字段。除非root在options对象中设置了选项,path否则必须是文件的绝对路径。
donwload文档描述
Transfers the file at path as an “attachment”. Typically, browsers will prompt the user for download.
By default, the Content-Disposition header “filename=” parameter is path (this typically appears in the browser dialog).
Override this default with the filename parameter.
sendFile告知浏览器它是一个流,但没让浏览器下载它。而download在响应头里加上了Content-Disposition: attachment; filename="xxxx" 告知浏览器需要下载。
这是mdn上的介绍
When used in combination with Content-Disposition: attachment, it is used as the default filename for an eventual "Save As" dialog presented to the user.