nginx文件预览下载
隔壁老瓦 人气:0@date: 2020-07-31 06:00
基于 Nginx + Java(SpringBoot) 实现带权限验证的静态文件服务器,支持文件下载、PDF预览和图片预览。
需要注意的是,无需权限判断的图片不建议使用此方法,大量的图片访问会增加后台服务器的处理压力。
一、实现原理
本质上是使用了X-Sendfile
功能来实现,X-Sendfile
是一种将文件下载请求重定向到Web 服务器处理的机制,该Web服务器只需负责处理请求(例如权限验证),而无需执行读取文件并发送给用户的任务。
X-Sendfile
可显著提高后台服务器的性能,消除了后端程序既要读文件又要处理发送的压力,尤其是处理大文件下载的情形下!
Nginx
也具有此功能,但实现方式略有不同。在Nginx中,此功能称为X-Accel-Redirect
。
用户请求文件,权限控制时序图:
二、实现步骤
1. NGINX配置
1、静态文件通过
file_server
访问,会被设置为internal
,即只能内部访问不允许外部直接访问。
2、所有静态资源请求均被重定向到Java后台,经过权限验证后才能访问。
# 文件下载服务 location ^~ /file_server { # 内部请求(即一次请求的Nginx内部请求),禁止外部访问,重要。 internal; # 文件路径 alias U:/file/private/; limit_rate 200k; # 浏览器访问返回200,然后转由后台处理 error_page 404 =200 @backend; } # 文件下载鉴权 location @backend { # 去掉访问路径中的 /file_server/,然后定义新的请求地址。 rewrite ^/file_server/(.*)$ /uecom/attach/$1 break; # 这里的url后面不可以再拼接地址 proxy_pass http://192.168.12.68:9023; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }
2. JAVA SPRINGBOOT 后台权限验证
用户请求只需要传递附件参数和身份token,不再需要传递服务器的真实路径。例如:
http://192.168.12.68:9022/file_server/preview_file?id=13e9d1887b46455a96842c503c2434cb&access_token=eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ3ZWJfMSIsImV4cCI6MTU5NjE1NzkxOH0.eR4yYlDjIMXZmNNTq2gdghkYhw6I30NgZlvuPUmRoyk
2.1 权限校验文件下载
response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath());
,重点是X-Accel-Redirect
配置返回服务器文件的真实路径,该路径返回后由Nginx内部请求处理,不会暴露给请求用户。
/** * @describe 使用token鉴权的文件下载 * @author momo * @date 2020-7-30 13:44 * @param id 文件唯一编码 uuid * @return void */ @GetMapping("/download_file") public void downloadFile(@NotNull String id) throws IOException { HttpServletResponse response = super.getHttpServletResponse(); // 通过唯一编码查询附件 Attach attach = attachService.getById(id); if (attach == null) { // 附件不存在,跳转404 this.errorPage(404); return; } // 从访问token中获取用户id。 token也可以通过 参数 access_token 传递 Integer userId = UserKit.getUserId(); if (userId == null || ) { // 无权限访问,跳转403 this.errorPage(403); return; } // 已被授权访问 // 文件下载 response.setHeader("Content-Disposition", "attachment; filename=\"" + new String(attach.getAttachName().getBytes("GBK"), "iso-8859-1") + "\""); // 文件以二进制流传输 response.setHeader("Content-Type", "application/octet-stream;charset=utf-8"); // 返回真实文件路径交由 Nginx 处理,保证前端无法看到真实的文件路径。 // 这里的 "/file_server" 为 Nginx 中配置的下载服务名 response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath()); // 限速,单位字节,默认不限 // response.setHeader("X-Accel-Limit-Rate","1024"); // 是否使用Nginx缓存,默认yes // response.setHeader("X-Accel-Buffering","yes"); response.setHeader("X-Accel-Charset", "utf-8"); // 禁止浏览器缓存 response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "No-cache"); response.setHeader("Expires", "0"); }
后台可配置属性:
Content-Type: Content-Disposition: : Accept-Ranges: Set-Cookie: Cache-Control: Expires: # 设置文件真实路径的URI,默认void X-Accel-Redirect: void # 限制下载速度,单位字节。默认不限速度off。 X-Accel-Limit-Rate: 1024|off # 设置此连接的代理缓存,将此设置为no将允许适用于Comet和HTTP流式应用程序的无缓冲响应。将此设置为yes将允许响应被缓存。默认yes。 X-Accel-Buffering: yes|no # 如果已传输过的文件被缓存下载,设置Nginx文件缓存过期时间,单位秒,默认不过期 off。 X-Accel-Expires: off|seconds # 设置文件字符集,默认utf-8。 X-Accel-Charset: utf-8
2.2 权限校验文件预览
文件下载和文件预览本质上没有区别,只是response
返回时告诉浏览器执行下载还是执行打开预览。即response.setHeader("Content-Disposition", "inline; filename="xxx.pdf");
, 然后修改返回数据的格式Content-Type
即可。
Content-Disposition 响应头指示回复的内容该以何种形式展示,是以内联(
inline
)的形式(即网页或者页面的一部分),还是以附件(attachment
)的形式下载并保存到本地。
/** * @describe 使用token鉴权的文件预览(支持pdf和图片) * @author momo * @date 2020-7-30 13:44 * @param id 文件唯一编码 uuid * @return void */ @GetMapping("/preview_file") public void previewFile(@NotNull String id) throws IOException { HttpServletResponse response = super.getHttpServletResponse(); // 通过唯一编码查询附件 Attach attach = attachService.getById(id); if (attach == null) { // 附件不存在,跳转404 this.errorPage(404); return; } // 从访问token中获取用户id。 token也可以通过 参数 access_token 传递 Integer userId = UserKit.getUserId(); if (userId == null || ) { // 无权限访问,跳转403 this.errorPage(403); return; } // 已被授权访问 // 文件直接显示 response.setHeader("Content-Disposition", "inline; filename=\"" + new String(attach.getAttachName().getBytes("GBK"), "iso-8859-1") + "\""); if ("pdf".equals(attach.getAttachType().toLowerCase())) { // PDF response.setHeader("Content-Type", "application/pdf;charset=utf-8"); } else { // 图片 response.setHeader("Content-Type", "image/*;charset=utf-8"); } // 返回真实文件路径交由 Nginx 处理,保证前端无法看到真实的文件路径。 // 这里的 "/file_server" 为 Nginx 中配置的下载服务名 response.setHeader("X-Accel-Redirect", "/file_server" + attach.getAttachPath()); // 浏览器缓存 1 小时 response.setDateHeader("Expires", System.currentTimeMillis() + 1000 * 60 * 60); }
三、扩展功能
1. 下载统计、访问日志
// 自由发挥
2. 下载限速
// 限速,单位字节,默认不限 response.setHeader("X-Accel-Limit-Rate","1024");
3. 防盗链
//判断 Referer String referer = request.getHeader("Referer"); if (referer == null || !referer.startsWith("https://www.itmm.wang")) { // 无权限访问,跳转403 this.errorPage(403); return; }
4. X-SENDFILE
X-Sendfile
是一项功能,每个代理服务器都有自己不同的实现。
Web Server | Header |
---|---|
Nginx | X-Accel-Redirect |
Apache | X-Sendfile |
Lighttpd | X-LIGHTTPD-send-file |
Squid | X-Accelerator-Vary |
使用 X-SendFile
的缺点是你失去了对文件传输机制的控制。例如如果你希望在完成文件下载后执行某些操作,比如只允许用户下载文件一次,这个 X-Sendfile 是没法做到的,因为请求交给了后台,你并不知道下载是否成功。
参考资料
加载全部内容