Jetty中异步发送文件

Jetty中异步发送文件
如果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();
        
        

    }

}


你可能感兴趣的:(Jetty中异步发送文件)