转自:http://denger.iteye.com/blog/1014066

基于 Nginx XSendfile + SpringMVC 进行文件下载

PS:经过实际测试,通过 nginx 提供文件下载功能的时候,在 Application Server(Java/RoR/Go...) 端不设置 Content-Length 也是可以的

在平常我们实现文件下载通常是通过普通 read-write方式,如下代码所示。

  1. @RequestMapping("/courseware/{id}")
  2. public void download(@PathVariable("id") String courseID, HttpServletResponse response) throws Exception {
  3. ResourceFile file = coursewareService.downCoursewareFile(courseID);
  4. response.setContentType(file.getType());
  5. response.setContentLength(file.contentLength());
  6. response.setHeader("Content-Disposition","attachment; filename=\"" + file.getFilename() +"\"");
  7. //Reade File - > Write To response
  8. FileCopyUtils.copy(file.getFile(), response.getOutputStream());
  9. }

由于程序的IO都是调用系统底层IO进行文件操作,于是这种方式在read和write时系统都会进行两次内存拷贝(共四次)。linux 中引入的 sendfile 的实际就为了更好的解决这个问题,从而实现"零拷贝",大大提升文件下载速度。

    使用 sendfile() 提升网络文件发送性能

    RoR网站如何利用lighttpd的X-sendfile功能提升文件下载性能

  



    在apache,nginx,lighttpd等web服务器当中,都有sendfile feature。下面就对 nginx 上的XSendfile与SpringMVC文件下载及访问控制进行说明。我们这里的大体流程为:

     1.用户发起下载课件请求; (http://dl.mydomain.com/download/courseware/1)

     2.nginx截获到该(dl.mydomain.com)域名的请求;

     3.将其proxy_pass至应用服务器;

     4.应用服务器根据课件id获取文件存储路径等其它一些业务逻辑(如增加下载次数等);

     5.如果允许下载,则应用服务器通过setHeader -> X-Accel-Redirect 将需要下载的文件转发至nginx中);

     6.Nginx获取到header以sendfile方式从NFS读取文件并进行下载。



     其nginx中的配置为:

     在location中加入以下配置

  1. server {
  2. listen 80;
  3. server_name dl.mydomain.com;
  4. location / {
  5. proxy_pass  http://127.0.0.1:8080/;  #首先pass到应用服务器
  6. proxy_redirect     off;
  7. proxy_set_header   Host             $host;
  8. proxy_set_header   X-Real-IP        $remote_addr;
  9. proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
  10. client_max_body_size       10m;
  11. client_body_buffer_size    128k;
  12. proxy_connect_timeout      90;
  13. proxy_send_timeout         90;
  14. proxy_read_timeout         90;
  15. proxy_buffer_size          4k;
  16. proxy_buffers              4 32k;
  17. proxy_busy_buffers_size    64k;
  18. proxy_temp_file_write_size 64k;
  19. }
  20. location /course/ {
  21. charset utf-8;
  22. alias       /nfs/files/; #文件的根目录(允许使用本地磁盘,NFS,NAS,NBD等)
  23. internal;
  24. }
  25. }

其Spring代码为:

  1. package com.xxxx.portal.web;
  2. import java.io.IOException;
  3. import java.io.UnsupportedEncodingException;
  4. import javax.servlet.http.HttpServletResponse;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Controller;
  7. import org.springframework.web.bind.annotation.PathVariable;
  8. import org.springframework.web.bind.annotation.RequestMapping;
  9. import com.xxxx.core.io.ResourceFile;
  10. import com.xxxx.portal.services.CoursewareService;
  11. /**
  12. * File download controller, provide courseware download or other files. <br>
  13. * <br>
  14. * <i> download a course URL e.g:<br>
  15. * http://dl.mydomain.com/download/courseware/1 </i>
  16. *
  17. * @author denger
  18. */
  19. @Controller
  20. @RequestMapping("/download/*")
  21. public class DownloadController {
  22. private CoursewareService coursewareService;
  23. protected static final String DEFAULT_FILE_ENCODING = "ISO-8859-1";
  24. /**
  25. * Under the courseware id to download the file.
  26. *
  27. * @param courseID The course id.
  28. * @throws IOException
  29. */
  30. @RequestMapping("/courseware/{id}")
  31. public void downCourseware(@PathVariable("id") String courseID, final HttpServletResponse response) throws IOException {
  32. ResourceFile file = coursewareService.downCoursewareFile(courseID);
  33. if (file != null && file.exists()){
  34. // redirect file to x-accel-Redirect
  35. xAccelRedirectFile(file, response);
  36. } else { // If not found resource file, send the 404 code
  37. response.sendError(404);
  38. }
  39. }
  40. protected void xAccelRedirectFile(ResourceFile file, HttpServletResponse response)
  41. throws IOException {
  42. String encoding = response.getCharacterEncoding();
  43. response.setHeader("Content-Type", "application/octet-stream");
  44. //这里获取到文件的相对路径。其中 /course/ 为虚拟路径,主要用于nginx中进行拦截包含了/course/ 的URL, 并进行文件下载。
  45. //在以上nginx配置的第二个location 中同样也设置了 /course/,实际的文件下载路径并不会包含 /course/
  46. //当然,如果希望包含的话可以将以上的 alias 改为 root 即可。
  47. response.setHeader("X-Accel-Redirect", "/course/"
  48. + toPathEncoding(encoding, file.getRelativePath()));
  49. response.setHeader("X-Accel-Charset", "utf-8");
  50. response.setHeader("Content-Disposition", "attachment; filename="
  51. + toPathEncoding(encoding, file.getFilename()));
  52. // response.setContentLength((int) file.contentLength());  // 经过实际测试,这里不设置 Content-Length 也是可以的
  53. }
  54. //如果存在中文文件名或中文路径需要对其进行编码成 iSO-8859-1
  55. //否则会导致 nginx无法找到文件及弹出的文件下载框也会乱码
  56. private String toPathEncoding(String origEncoding, String fileName) throws UnsupportedEncodingException{
  57. return new String(fileName.getBytes(origEncoding), DEFAULT_FILE_ENCODING);
  58. }
  59. @Autowired
  60. public void setCoursewareService(CoursewareService coursewareService) {
  61. this.coursewareService = coursewareService;
  62. }
  63. }

基于 Nginx XSendfile + SpringMVC 进行文件下载的更多相关文章

  1. 【转载】【JAVA秒会技术之图片上传】基于Nginx及FastDFS,完成图片的上传及展示

    基于Nginx及FastDFS,完成商品图片的上传及展示 一.传统图片存储及展示方式 存在问题: 1)大并发量上传访问图片时,需要对web应用做负载均衡,但是会存在图片共享问题 2)web应用服务器的 ...

  2. 基于Nginx dyups模块的站点动态上下线并实现简单服务治理

    简介 今天主要讨论一下,对于分布式服务,站点如何平滑的上下线问题. 分布式服务 在分布式服务下,我们会用nginx做负载均衡, 业务站点访问某服务站点的时候, 统一走nginx, 然后nginx根据一 ...

  3. 基于nginx tomcat redis分布式web应用的session共享配置

    一.前言 nginx 作为目前最流行的开源反向代理HTTP Server,用于实现资源缓存.web server负载均衡等功能,由于其轻量级.高性能.高可靠等特点在互联网项目中有着非常普遍的应用,相关 ...

  4. 基于nginx的tomcat负载均衡和集群

    要集群tomcat主要是解决SESSION共享的问题,因此我利用memcached来保存session,多台TOMCAT服务器即可共享SESSION了. 你可以自己写tomcat的扩展来保存SESSI ...

  5. 转: 基于nginx的hls直播系统

    转自:http://blog.csdn.net/cjsafty/article/details/9108587 看点: 1. 详细解解答了 nginx rtmp配置过程. 前写了一篇基于nginx的h ...

  6. Windows 环境下基于 nginx 的本地 PyPI 源

    Windows 环境下基于 nginx 的本地 PyPI 源的搭建: 1.登录 nginx 官网,下载安装包

  7. 7个基于Linux命令行的文件下载和网站浏览工具

    7个基于Linux命令行的文件下载和网站浏览工具 时间:2015-06-01 09:36来源:linux.cn 编辑:linux.cn 点击: 2282 次 Linux命令行是GNU/Linux中最神 ...

  8. 基于nginx+lua+redis高性能api应用实践

    基于nginx+lua+redis高性能api应用实践 前言 比较传统的服务端程序(PHP.FAST CGI等),大多都是通过每产生一个请求,都会有一个进程与之相对应,请求处理完毕后相关进程自动释放. ...

  9. Ubuntu 14.10下基于Nginx搭建mp4/flv流媒体服务器(可随意拖动)并支持RTMP/HLS协议(含转码工具)

    Ubuntu 14.10下基于Nginx搭建mp4/flv流媒体服务器(可随意拖动)并支持RTMP/HLS协议(含转码工具) 最近因为项目关系,收朋友之托,想制作秀场网站,但是因为之前一直没有涉及到这 ...

随机推荐

  1. hibernate 普通字段延迟载入无效的解决的方法

    关联对象的延迟载入就不说了.大家都知道. 关于普通字段的延迟载入,尤其是lob字段,若没有延迟载入,对性能影响极大.然而简单的使用 @Basic(fetch = FetchType.LAZY) 注解并 ...

  2. STL在迭代的过程中,删除指定的元素

    直接上Code,上 Picture #include <iostream> #include <list> using namespace std; // STL在迭代的过程中 ...

  3. C++高精度性能測试函数

    在实际software开发工作中.我们常常会測试某个module或者function的执行效率.或者是某个算法的时间复杂度(尽管时间复杂度一定程度上依赖于机器性能.但在同一台computer上,经过算 ...

  4. 蒟蒻的数位DP专题总结

    BZOJ  1026: [SCOI2009]windy数: 题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=1026           d ...

  5. luogu1980 车站分级

    题目大意 一些火车站排成一行.给出一些火车的停靠站情况,要求对每一个火车,其经过且不停靠的站的级别比它任意停靠的站的级别小.问所有车站最少需要多少个级别. 题解 不要只看到这道题的背景设立在一个区间上 ...

  6. Android WiFi/WiFi热点开发总结

    首先看一下WiFi的自我介绍: Wi-Fi是一种允许电子设备连接到一个无线局域网(WLAN)的技术,通常使用2.4G UHF或5G SHF ISM 射频频段.连接到无线局域网通常是有密码保护的:但也可 ...

  7. java input 实现调用手机相机和本地照片上传图片到服务器然后压缩

    在微信公众号里面需要上传头像,时间比较紧,调用学习jssdk并使用 来不及  就用了input 使用input:file标签, 去调用系统默认相机,摄像,录音功能,其实是有个capture属性,直接说 ...

  8. POJ 3468 线段树+状压

    题意:给你n个数,有对区间的加减操作,问某个区间的和是多少. 思路:状压+线段树(要用lazy标记,否则会TLE) //By SiriusRen #include <cstdio> #in ...

  9. js数据管理的思考

    最近要做一个农场项目,涉及到很多js数据管理的需求,这里也做下总结,不断的总结,再修正内容,也是快速进步的方法. 数据管理几个方面考虑: * 设置(更新)字段值 * 检索,根据id, index, 属 ...

  10. Windows phone开发 网络编程之HttpWebRequest

    HttpWebRequest和WebClient的区别1,HttpWebRequest是个抽象类,所以无法new的,需要调用HttpWebRequest.Create();2,其Method指定了请求 ...