Jetty中异步发送文件
如果Web服务器需要频繁传送文件给客户端时,大多数web容器会提供异步发送的支持,像IIS中的通过ServerSupportFunction(),Apache里面apr_sendfile(),Tomcat6.X通过设置Servlet Request的请求属性等等。。。都可以做到异步发送文件,从而提高服务器性能。
Jetty6.X默认也不提供相关支持,本文提供一种hack方法,仅供参考:
先看测试Servlet:
下面是通过firefox直输入地址回车后,下载文件,后台的程序运行结果:
当运行2时,客户端下完代码才执行完。
以下是Jetty异步发送的代码:
正常发送文件的代码:
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时使用正常方式。
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);
}
}
}
下面是通过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的操作是异步的。
service ok, action = 2 ,use: 10688 !!!
service ok, action = 2 ,use: 9063 !!!
service ok, action = 1 ,use: 47 !!!
当运行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);
}
}
}
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();
}
}
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();
}
}