在项目中有一个文件是服务器端打包生成后的文件放在服务器中某一个目录内,文件的特点如下:
传统做法很容易,采用SpringBoot建立API,采用Token等方式对用户权限鉴定后,读取文件流,由Java发送给客户端。这样不暴露静态HTTP链接给用户,解决了鉴权问题。但是由于文件大用户网速慢并且不稳定等各种原因实际效果并不理想。主要问题如下:
思路:能否结合让静态文件下载由类似nginx这样的web服务器去进行,并有一鉴权功能。方案当然是有的,比如采用lua或者自己编写nginx插件,但是改动都比较大成本非常高,查阅文档后发现可以采用如下x-sendfile这中方式来实现。
X-Sendfile 通过一个特定的 HTTP header 来实现:在 X-Sendfile 头中指定一个文件的地址来通告前端 web 服务器。当 web 服务器检测到后端发送的这个 header 后,它将忽略后端的其他输出,而使用自身的组件(包括 缓存头 和 断点重连 等优化)机制将文件发送给用户。
在使用 X-Sendfile 之前,我们必须明白这并不是一个标准特性,在默认情况下它是被大多数 web 服务器禁用的。而不同的 web 服务器的实现也不一样,包括规定了不同的 X-Sendfile 头格式。如果配置失当用户可能会下载到错误的文件。
SENDFILE 头 | 使用的 WEB 服务器 |
---|---|
X-Sendfile | Apache, Lighttpd v1.5, Cherokee |
X-LIGHTTPD-send-file | Lighttpd v1.4 |
X-Accel-Redirect | Nginx, Cherokee |
缺点:由于发送下载文件由nginx做了,所以Java是无法知道文件下载完成的
这里主要说明nginx的X-SENDFILE配置。
在nginx配置文件中设置如下:
location /protected/ {
internal;
root /some/path;
}
internal指令保证了这个链接只能被nginx自己访问,root可以映射目录。
Nginx 配置
# 动态权限
locaton /api/ {
proxy_pass http://upstream:8080/;
}
#内部静态地址
location /download/ {
internal;
root /some/path;
}
注意: /some/path/donwload文件夹必须存在
这里以Springboot Java代码作为例子
@RequestMapping(value = "/api/download", method = RequestMethod.GET)
public void plistDownLoad(HttpServletResponse response,
@RequestParam("bidSectionID") String bidSectionID,
@RequestParam("token") String token,
HttpServletResponse response) {
// 权限代码
if(authService.auth(token)) {
String filePath = fileService.getFilte(bidSetionID)
// 上面返回 /download/test.zip
Stirng outFileName = FileNameUitls.getFileName(filePath);
String contentDisposition = "attachment" + ";filename*=UTF-8''" + URLEncoder.encode(outFileName, "UTF-8");
response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
response.setHeader("Content-Disposition", contentDisposition);
response.setHeader("X-Accel-Redirect", filePath);
} else {
// 报错
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=UTF-8");
PrintWriter pw = response.getWriter();
pw.write("非法访问");
pw.flush();
pw.close();
}
}
本文为Lokie.Wang原创文章,转载无需和我联系,但请注明来自lokie博客http://lokie.wang