当前位置:首页 > 开发 > 系统架构 > 架构 > 正文

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

发表于: 2011-04-24   作者:denger   来源:转载   浏览:
摘要:     在平常我们实现文件下载通常是通过普通 read-write方式,如下代码所示。 @RequestMapping("/courseware/{id}") public void download(@PathVariable("id") String courseID, HttpServletResp
    在平常我们实现文件下载通常是通过普通 read-write方式,如下代码所示。
 
   @RequestMapping("/courseware/{id}") 
   public void download(@PathVariable("id") String courseID, HttpServletResponse response) throws Exception {

        ResourceFile file = coursewareService.downCoursewareFile(courseID);
        response.setContentType(file.getType());
        response.setContentLength(file.contentLength());
        response.setHeader("Content-Disposition","attachment; filename=\"" + file.getFilename() +"\"");
        //Reade File - > Write To response
        FileCopyUtils.copy(file.getFile(), response.getOutputStream());
    }

    由于程序的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中加入以下配置
     
server {
        listen 80;
        server_name dl.mydomain.com;

        location / {
            proxy_pass  http://127.0.0.1:8080/;  #首先pass到应用服务器
            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;

            client_max_body_size       10m;
            client_body_buffer_size    128k;

            proxy_connect_timeout      90;
            proxy_send_timeout         90;
            proxy_read_timeout         90;

            proxy_buffer_size          4k;
            proxy_buffers              4 32k;
            proxy_busy_buffers_size    64k;
            proxy_temp_file_write_size 64k;

        }

        location /course/ { 
            charset utf-8;
            alias       /nfs/files/; #文件的根目录(允许使用本地磁盘,NFS,NAS,NBD等)
            internal;
        }
    }


    其Spring代码为:
   
package com.xxxx.portal.web;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.xxxx.core.io.ResourceFile;
import com.xxxx.portal.services.CoursewareService;

/**
 * File download controller, provide courseware download or other files. <br>
 * <br>
 * <i> download a course URL e.g:<br>
 * http://dl.mydomain.com/download/courseware/1 </i>
 * 
 * @author denger
 */
@Controller
@RequestMapping("/download/*")
public class DownloadController {

	private CoursewareService coursewareService;
	
	protected static final String DEFAULT_FILE_ENCODING = "ISO-8859-1";

	/**
	 * Under the courseware id to download the file.
	 * 
	 * @param courseID The course id.
	 * @throws IOException 
	 */
	@RequestMapping("/courseware/{id}")
	public void downCourseware(@PathVariable("id") String courseID, final HttpServletResponse response) throws IOException {
		ResourceFile file = coursewareService.downCoursewareFile(courseID);
		if (file != null && file.exists()){
			// redirect file to x-accel-Redirect 
			xAccelRedirectFile(file, response);

		} else { // If not found resource file, send the 404 code
			response.sendError(404);
		}
	}

	protected void xAccelRedirectFile(ResourceFile file, HttpServletResponse response) 
		throws IOException {
		String encoding = response.getCharacterEncoding();

		response.setHeader("Content-Type", "application/octet-stream");
        //这里获取到文件的相对路径。其中 /course/ 为虚拟路径,主要用于nginx中进行拦截包含了/course/ 的URL, 并进行文件下载。
        //在以上nginx配置的第二个location 中同样也设置了 /course/,实际的文件下载路径并不会包含 /course/
        //当然,如果希望包含的话可以将以上的 alias 改为 root 即可。
		response.setHeader("X-Accel-Redirect", "/course/"
				+ toPathEncoding(encoding, file.getRelativePath()));
		response.setHeader("X-Accel-Charset", "utf-8");

		response.setHeader("Content-Disposition", "attachment; filename="
				+ toPathEncoding(encoding, file.getFilename()));
		response.setContentLength((int) file.contentLength());
	}

    //如果存在中文文件名或中文路径需要对其进行编码成 iSO-8859-1
    //否则会导致 nginx无法找到文件及弹出的文件下载框也会乱码
	private String toPathEncoding(String origEncoding, String fileName) throws UnsupportedEncodingException{
		return new String(fileName.getBytes(origEncoding), DEFAULT_FILE_ENCODING);
	}

	@Autowired
	public void setCoursewareService(CoursewareService coursewareService) {
		this.coursewareService = coursewareService;
	}
}

  
   

  
   

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

  • 0

    开心

    开心

  • 0

    板砖

    板砖

  • 0

    感动

    感动

  • 0

    有用

    有用

  • 0

    疑问

    疑问

  • 0

    难过

    难过

  • 0

    无聊

    无聊

  • 0

    震惊

    震惊

编辑推荐
需求 例如图示这种http请求,我现在有两种需求: 呈现图片 下载图片 例如这个url,nginx实现了浏览
今天学到点好用的技巧,即利用Nginx访问、下载本机目录文件,Mac下的具体做法如下: 一、安装Nginx
2016小强归来,高级性能测试实战精讲班开始招生(经过万人验证的课程体系),与51CTO学院强强联合(
不熟悉的可以去Org/Net/Http 看看Http.class.php类文件 一、使用curlDownload 采集远程文件 /** *
忙了好几天,终于小有成就,分享下,也供如我一般四处碰壁、头破血流的初学者参考。 首先说下我要实
iOS开发网络篇—使用ASI框架进行文件下载 说明:本文介绍iOS网络编程中经常用到的框架ASI,如何使用
function invokeClick(element) { if (element.click) element.click(); //判断是否支持click() 事
一、简单介绍 代码示例: 1 #import "YYViewController.h" 2 #import "ASIHTTPRequest.h" 3 4 @inte
第一部分 知识储备 1.对NSURLSesiion的认识 NSURLSesiion是苹果在iOS7推出的一个类,它具备了NSURLC
由于需要从某个网页上下载一些PDF文件,但是需要下载的PDF文件有几百个,所以不可能用人工点击来下
版权所有 IT知识库 CopyRight © 2009-2015 IT知识库 IT610.com , All Rights Reserved. 京ICP备09083238号