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);
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);
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', 'attachment; filename="loveBySendFile.jpg"');
res.sendFile('C:/Users/lenovo/Pictures/love.jpg');
});
好了,下载文件就这么多吧,十一放假在家学习效率莫名地高,祝大家节日快乐(不要问我为什么一会用 Edge,一会用 Google 浏览器,一会儿又用 FireFox,问就是我还有 Opera 没用呢😀,其实是不好截图,尤其是下载文件部分。
|