E81086713E446D36F62B2AA2A3502B5EB155

Java杂家

杂七杂八。。。一家之言

BlogJava 首页 新随笔 联系 聚合 管理
  141 Posts :: 1 Stories :: 174 Comments :: 0 Trackbacks
如果Web服务器需要频繁传送文件给客户端时,大多数web容器会提供异步发送的支持,像IIS中的通过ServerSupportFunction(),Apache里面apr_sendfile(),Tomcat6.X通过设置Servlet Request的请求属性等等。。。都可以做到异步发送文件,从而提高服务器性能。

Jetty6.X默认也不提供相关支持,本文提供一种hack方法,仅供参考:

先看测试Servlet:
package com.yovn.labs.testweb;

import java.io.File;
import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.yovn.labs.sendfile.JettySendFile;
import com.yovn.labs.sendfile.NormalSendFile;
import com.yovn.labs.sendfile.SendFile;

/**
 * 
@author yovn
 *
 
*/
public class SendFileServlet extends HttpServlet {

    
protected void doGet(HttpServletRequest req, HttpServletResponse res)
            
throws ServletException, IOException {
    
        
        
try {
            
            File f
=new File("D:\\workspace\\TEST.rar");//about 45M
            String action
=req.getParameter("action");
            SendFile sf
=null;
            
if("1".equals(action))
            {
                sf
=new JettySendFile();
            }
            
else
            {
                sf
=new NormalSendFile();
            }
            
long start=System.currentTimeMillis();
            sf.doSend(req, res, f, 
null);
            System.out.println(
" service ok, action="+action+",use:"+(System.currentTimeMillis()-start)+"!!!");
        } 
catch (Exception e) {
            
throw new ServletException(e);
        }
      
    }
    
    

}
代码很简单明了,action=1时使用Jetty的异步发送,action=2时使用正常方式。

下面是通过firefox直输入地址回车后,下载文件,后台的程序运行结果:
 service ok, action=1,use:62!!!
 service ok, action
=2,use:10688!!!
 service ok, action
=2,use:9063!!!
 service ok, action
=1,use:47!!!
当运行1时,实际上客户端还没有下完文件,但是该段代码已经执行完了,IO的操作是异步的。
当运行2时,客户端下完代码才执行完。

以下是Jetty异步发送的代码:
package com.yovn.labs.sendfile;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.mortbay.io.EndPoint;
import org.mortbay.io.nio.NIOBuffer;

/**
 * 
@author yovn
 *
 
*/
public class JettySendFile implements SendFile {

    
static Field endpointField=null;
    
static Class reqCls=null;
    
static 
    {
        
try {
            reqCls
=Class.forName("org.mortbay.jetty.Request");
            endpointField
=reqCls.getDeclaredField("_endp");
            endpointField.setAccessible(
true);
        } 
catch (Exception e) {
            
// TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }
        
    
    


    
public void doSend(HttpServletRequest req, HttpServletResponse res,File todo,String headerOpt)
            
throws IOException {
        
        
if(endpointField==null)throw new IOException("Jetty Not Available!!");
        
if(!req.getClass().equals(reqCls))
        {
            
throw new IOException("Not in Jetty Context!!");
        }
        EndPoint ep;
        
try {
            ep 
= (EndPoint)endpointField.get(req);
        
            
if(headerOpt==null)
            {
                headerOpt
="HTTP/1.1 200 OK \r\nContent-Type: APPLICATION/OCTET-STREAM\r\n"+
                      
"Content-Disposition: attachment;filename=\""+todo.getName()+"\"\r\n"+
                      
"Content-Length: "+todo.length()+"\r\n\r\n";
            }
            
byte[] headerBytes=headerOpt.getBytes("UTF-8");
            NIOBuffer header
=new NIOBuffer(headerBytes.length,false);
            header.put(headerBytes);
            
            NIOBuffer buffer
=new NIOBuffer(todo);
            
            ep.flush(header, buffer, 
null);
        } 
catch (IllegalArgumentException e) {
            
throw new IOException(e);
        } 
catch (IllegalAccessException e) {
            
throw new IOException(e);
        }
        
        
        
    }

}

正常发送文件的代码:
package com.yovn.labs.sendfile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 
@author yovn
 *
 
*/
public class NormalSendFile implements SendFile {

    
/* (non-Javadoc)
     * We simply ignore the 'headerOpt'
     * @see com.yovn.labs.sendfile.SendFile#doSend(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, java.io.File, java.lang.String)
     
*/
    
public void doSend(HttpServletRequest req, HttpServletResponse res,
            File todo, String headerOpt) 
throws IOException {
        res.setHeader(
"Content-Type""APPLICATION/OCTET-STREAM");
        res.setHeader(
"Content-Disposition""attachment;filename=\""+todo.getName()+"\"");
        res.setHeader(
"Content-Length", todo.length()+"");
        res.setStatus(HttpServletResponse.SC_OK);
        OutputStream out
=res.getOutputStream();
        InputStream in
=new FileInputStream(todo);
        
        
byte[] buffer=new byte[8192];
        
        
int read=0;
        
while((read=in.read(buffer))>0)
        {
            out.write(buffer, 
0, read);
        }
        out.flush();
        in.close();
        
        

    }

}


posted on 2008-03-29 01:54 DoubleH 阅读(1763) 评论(0)  编辑  收藏

只有注册用户登录后才能发表评论。


网站导航: