tomcat/spring怎么零拷贝的发送文件?

1、普通的发送文件方式

想要服务端发送文件,浏览器端下载文件,一般的写法是创建一个buffer不断读取文件,写到response里面,像这样

@Controller
public class TestController {
   

    @RequestMapping("/index")
    public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {
   
        String filename = "111.txt";
        String filePath = "src/main/resources/" + filename;

        File file = new File(filePath);
        FileInputStream fileInputStream = new FileInputStream(file);

        response.setHeader("Content-Disposition", "attachment; filename=" + filename);
        response.setHeader("Content-Length", String.valueOf(file.length()));

        ServletOutputStream outputStream = response.getOutputStream();
        byte[] buffer = new byte[1024];
        int cnt;
        while ((cnt = fileInputStream.read(buffer)) > 0) {
   
            outputStream.write(buffer, 0, cnt);
        }
    }
}

但是这样文件的发送是比较低效的,这里面涉及到两次上下文切换(用户态、内核态之间切换),CPU参与复制数据两次(从文件缓冲区复制到buffer,从buffer复制到socket缓冲区)。这篇文章介绍的比较好https://blog.csdn.net/qq_26323323/article/details/120244493

2、零拷贝的发送文件方式

实际上java NIO提供了比较高效的文件传输方式,FileChannel#transferTo,底层利用了linux sendfile优化,可以做到零拷贝,提高传输效率。tomcat利用了FileChannel#transferTo来提高文件传输效率,在spring里只需要这样

@Controller
public class TestController {
   

    @RequestMapping("/index")
    public void index(HttpServletRequest request, HttpServletResponse response) {
   
        String filename = "111.txt";
        String filePath = "src/main/resources/" + filename;

        File file = new File(filePath);

        // 设置文件下载的header。这里本身跟文件传输无关,只是这样写能够通过浏览器下载这个文件
        response.setHeader("Content-Disposition", "attachment; filename=" + filename);
        response.setHeader("Content-Length", String.valueOf(file.length()));

    	// 设置attrible,由tomcat处理文件传输
        request.setAttribute(Constants.SENDFILE_FILENAME_ATTR, filePath);
        request.setAttribute(Constants.SENDFILE_FILE_START_ATTR, 0L);
        request.setAttribute(Constants.SENDFILE_FILE_END_ATTR, file.length());
    }
}

尽管此种方式能够做到零拷贝,但是底层tomcat的实现原因,是不能完全支持Range语法的

3、tomcat实现方式

tomcat是怎么实现的呢?

Poller

在tomcat NioEndpoint里,Poller处理所有的客户端socket连接,通过nio的方式获取socket就绪事件并处理就绪事件。处理的过程中会检查是否存在sendFileData,存在的话会处理文件的发送
1、这个是poller线程主要逻辑,processKey处理每个就绪的socket

        public void run() {
   
            // Loop until destroy() is called
            while (true) {
   

                boolean hasEvents = false;

                try {
   
                    if (!close) {
   
                        hasEvents = events();
                        if (wakeupCounter.getAndSet(-1) > 0) {
   
                            // If we are here, means we have other stuff to do
                            // Do a non blocking select
                            keyCount = selector.selectNow();
                        } else {
   
                    
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值