Express の 文件下载
上一篇谈到了使用express
处理POST
请求。
文件下载
简单粗暴,下载文件就用res.download()
方法。(💊这个方法只能在 v4.16.0 之后使用…)
app.get('/download/:type', (req, res) => {
let type = req.params.type || '';
console.log(type);
if (type === 'pdf') {
res.download('C:/Users/lenovo/Desktop/if.pdf');
} else if (type === 'image') {
res.download('C:/Users/lenovo/Pictures/love.jpg');
} else {
res.download('../resource/index.html');
}
});
有几点需要注意的地方🤔
- 绝对路径或相对路径都可以。这不同
res.sendFile()
方法,后者只能使用绝对路径。 - windows 下目录分隔符是
\
,但是这里要使用/
这个方法其实还有其他参数,比如指定下载的文件名。
app.get('/download/:type', (req, res) => {
let type = req.params.type || '';
console.log(type);
if (type === 'pdf') {
res.download('C:/Users/lenovo/Desktop/if.pdf', '如果.pdf');
} else if (type === 'image') {
res.download('C:/Users/lenovo/Pictures/love.jpg', '爱.jpg');
} else {
res.download('../resource/index.html', '首页.html');
}
});
如果代码加上不生效,试试浏览器禁用缓存…(别问我怎么知道的😅)
再来,就是指定传输完成或发生异常时的回调函数
app.get('/download/:type', (req, res) => {
let type = req.params.type || '';
console.log(type);
if (type === 'pdf') {
res.download('C:/Users/lenovo/Desktop/if.pdf', '如果.pdf');
} else if (type === 'image') {
// 回调函数
res.download('C:/Users/lenovo/Pictures/love.jpg', '爱.jpg', (err) => {
if (err) {
console.log('下载内容出错', err);
} else {
console.log('传输完成');
}
});
} else {
res.download('../resource/index.html', '首页.html');
}
});
没有 v4.16.0 怎么办???
老兄劝你早点升级…(狗头
其实看download
方法的官方文档,作者说的很清楚,download
内部使用res.sendFile()
实现的,后者从 v4.8.0 支持的,如果你连 v4.8.0 也没有,听说隔壁SpringBoot
还在支持Java 8
,嗯~ o( ̄▽ ̄)o
res.sendFile()
自动根据文件的扩展名设置Content-Type
响应头。我们先尝试使用上面的绝对文件路径发送文件。
app.get('/file1', (req, res) => {
res.sendFile('C:/Users/lenovo/Pictures/love.jpg');
});
效果是图片直接展示在浏览器窗口
为什么会这样我们稍后再说。有一个值得思考的问题,如果我们提供很多个不同下载文件的接口,这些文件都是用绝对路径表示,维护起来就很难受,因为要多处修改。所以sendFile()
提供了其他第二个参数options
。
res.sendFile(path [, options] [, fn])
options
表示一个对象,其中有一个root
属性。如果调用函数时没有root
那就必须将path
写成绝对路径的形式;如果指定了root
那path
就可以使用相对路径,express
会帮我们解析文件的最终位置。整个目录是这样的
app.get('/file2', (req, res) => {
console.log(__dirname); // C:\Users\lenovo\Desktop\css\node\express
let options = {
root: path.join(__dirname, '../resource')
};
res.sendFile('love.jpg', options);
});
首先__dirname
是Node.js
中当前js
文件绝对路径的表示。在代码中我们使用path
核心库拼接下载文件的根路径path.join(__dirname, '../resource')
。其实…我第一次不是这么写的…
app.get('/file3', (req, res) => {
console.log(__dirname); // C:\Users\lenovo\Desktop\css\node\express
let options = {
root: path.join(__dirname, 'resource')
};
res.sendFile('../resource/love.jpg', options);
});
这样写会报错ForbiddenError: Forbidden
,虽然官网真的说了可以使用相对路径包括..
(虽然我翻车了,但是真的没骗人)
下面我们就说一下,为什么使用sentFile()
文件直接展示在窗口,而不是下载?
内容协商
内容协商我自己觉得是个很大的内容,说实话在研究上面这个问题之前,我竟然毫不知内容协商是干嘛的,以后再也不说我要转前端了…
在 HTTP 协议中,内容协商是这样一种机制,通过为同一 URI 指向的资源提供不同的展现形式,可以使用户代理选择与用户需求相适应的最佳匹配. – MDN 官网
如果用人话来说,可以简单理解成,假如浏览器通过 URL 请求一段文字,服务端向英语用户返回英文,向中文用户返回中文,这样一个网络资源(文字)就只对应一个 URL。服务端怎么这么智能呢?那是因为浏览器请求时会发送一系列Accept
的请求头;浏览器咋知道是英文还是中文呢?那是因为响应包括一系列Content
的响应头。(下图来自 MDN 官网)
用我们上面例子来说,图片没有直接下载而是展示在窗口,就是响应头Content-Disposition
捣的鬼😕
Content-Disposition
告诉浏览器返回给你的内容究竟如何展示
inline
: 作为网页的一部分直接展示在浏览器(默认)attachment
: 作为 附件 直接下载。后面可以加上; filename=
指定下载的文件名。
因为默认属性是inline
,所以图片直接展示了,这个功能是不是有点熟悉,没错,一般浏览器对着图片右键就会出现在新标签页中打开图片
的选项…就可以这样做
那接下来我们修改代码,即可做到使用sendFile()
下载文件。
app.get('/file4', (req, res) => {
res.setHeader('Content-Type', 'image/jpeg');
// res.setHeader('Content-Disposition', 'inline');
res.setHeader('Content-Disposition', 'attachment; filename="loveBySendFile.jpg"');
res.sendFile('C:/Users/lenovo/Pictures/love.jpg');
});
好了,下载文件就这么多吧,十一放假在家学习效率莫名地高,祝大家节日快乐(不要问我为什么一会用 Edge,一会用 Google 浏览器,一会儿又用 FireFox,问就是我还有 Opera 没用呢😀,其实是不好截图,尤其是下载文件部分。