使用Netty进行文件传输

本文详细介绍了使用Netty进行文件传输的实现过程,包括客户端和服务器端的代码解析,并深入探讨了Netty的零拷贝功能在大文件传输中的应用。通过实践案例,展示了如何优化文件传输效率,特别强调了Netty官网例子的重要性及其提供的直观指导。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在写出了Netty Hello World 和 netty对象传输之后,又觉得不够,看了官网的例子,所以有了现在的这个文件传输。
顺便说下,netty官网的例子真的好,如果要学习netty,还是看官网例子的好。
不过我英文不太好,刚开始走了绕了好大一圈,但是现在熟悉了之后,回过头来看,还是官网的牛X。
在这里再说下netty的零拷贝,这个零拷贝是netty在3.2版本中新加入的功能。
其主要指的是在进行一些比较大的传输比如对象或者文件传输的时候,通过改变数组索引的方式,将数据传输到特定的channel上。
是的channel之间的转换是零拷贝,例如:ByteBuffer和ChannelBuffer
下面就把文件传输的例子贴出来吧。
1、FileClient.java

  1. package filetrans;  
  2.   
  3. import static org.jboss.netty.channel.Channels.pipeline;  
  4.   
  5. import java.net.InetSocketAddress;  
  6. import java.util.concurrent.Executors;  
  7.   
  8. import org.jboss.netty.bootstrap.ClientBootstrap;  
  9. import org.jboss.netty.channel.ChannelFuture;  
  10. import org.jboss.netty.channel.ChannelPipeline;  
  11. import org.jboss.netty.channel.ChannelPipelineFactory;  
  12. import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;  
  13. import org.jboss.netty.handler.codec.http.DefaultHttpRequest;  
  14. import org.jboss.netty.handler.codec.http.HttpMethod;  
  15. import org.jboss.netty.handler.codec.http.HttpRequest;  
  16. import org.jboss.netty.handler.codec.http.HttpRequestEncoder;  
  17. import org.jboss.netty.handler.codec.http.HttpResponseDecoder;  
  18. import org.jboss.netty.handler.codec.http.HttpVersion;  
  19. import org.jboss.netty.handler.stream.ChunkedWriteHandler;  
  20.   
  21. public class FileClient  
  22. {  
  23.     public static void main(String[] args)  
  24.     {  
  25.   
  26.         ClientBootstrap bootstrap = new ClientBootstrap(  
  27.                 new NioClientSocketChannelFactory(  
  28.                         Executors.newCachedThreadPool(),  
  29.                         Executors.newCachedThreadPool()));  
  30.         bootstrap.setPipelineFactory(new ChannelPipelineFactory()  
  31.         {  
  32.   
  33.             @Override  
  34.             public ChannelPipeline getPipeline() throws Exception  
  35.             {  
  36.                 ChannelPipeline pipeline = pipeline();  
  37.   
  38.                 pipeline.addLast("decoder"new HttpResponseDecoder());  
  39.                   
  40.                 /* 
  41.                  * 不能添加这个,对传输文件 进行了大小的限制。。。。。 
  42.                  */  
  43. //                pipeline.addLast("aggregator", new HttpChunkAggregator(6048576));  
  44.                 pipeline.addLast("encoder"new HttpRequestEncoder());  
  45.                 pipeline.addLast("chunkedWriter"new ChunkedWriteHandler());  
  46.                 pipeline.addLast("handler"new FileClientHandler());  
  47.   
  48.                 return pipeline;  
  49.             }  
  50.   
  51.         });  
  52.   
  53.         ChannelFuture future = bootstrap.connect(new InetSocketAddress(  
  54.                 "localhost"8080));  
  55.   
  56.         /* 
  57.          * 这里为了保证connect连接,所以才进行了sleep 
  58.          * 当然也可以通过future的connect属性判断 
  59.          */  
  60.         try  
  61.         {  
  62.             Thread.sleep(3000);  
  63.         } catch (InterruptedException e)  
  64.         {  
  65.             e.printStackTrace();  
  66.         }  
  67.         HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1,  
  68.                 HttpMethod.GET, "DSC01575.JPG");  
  69.         future.getChannel().write(request);  
  70.   
  71.         // Wait until the connection is closed or the connection attempt fails.  
  72.         future.getChannel().getCloseFuture().awaitUninterruptibly();  
  73.   
  74.         // Shut down thread pools to exit.  
  75.         bootstrap.releaseExternalResources();  
  76.   
  77.     }  
  78. }  
package filetrans;

import static org.jboss.netty.channel.Channels.pipeline;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;

public class FileClient
{
    public static void main(String[] args)
    {

        ClientBootstrap bootstrap = new ClientBootstrap(
                new NioClientSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));
        bootstrap.setPipelineFactory(new ChannelPipelineFactory()
        {

            @Override
            public ChannelPipeline getPipeline() throws Exception
            {
                ChannelPipeline pipeline = pipeline();

                pipeline.addLast("decoder", new HttpResponseDecoder());
                
                /*
                 * 不能添加这个,对传输文件 进行了大小的限制。。。。。
                 */
//                pipeline.addLast("aggregator", new HttpChunkAggregator(6048576));
                pipeline.addLast("encoder", new HttpRequestEncoder());
                pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());
                pipeline.addLast("handler", new FileClientHandler());

                return pipeline;
            }

        });

        ChannelFuture future = bootstrap.connect(new InetSocketAddress(
                "localhost", 8080));

        /*
         * 这里为了保证connect连接,所以才进行了sleep
         * 当然也可以通过future的connect属性判断
         */
        try
        {
            Thread.sleep(3000);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
                HttpMethod.GET, "DSC01575.JPG");
        future.getChannel().write(request);

        // Wait until the connection is closed or the connection attempt fails.
        future.getChannel().getCloseFuture().awaitUninterruptibly();

        // Shut down thread pools to exit.
        bootstrap.releaseExternalResources();

    }
}

2、FileClientHandler.java

  1. package filetrans;  
  2.   
  3. import java.io.File;  
  4. import java.io.FileOutputStream;  
  5.   
  6. import org.jboss.netty.buffer.ChannelBuffer;  
  7. import org.jboss.netty.channel.ChannelHandlerContext;  
  8. import org.jboss.netty.channel.ExceptionEvent;  
  9. import org.jboss.netty.channel.MessageEvent;  
  10. import org.jboss.netty.channel.SimpleChannelUpstreamHandler;  
  11. import org.jboss.netty.handler.codec.http.DefaultHttpResponse;  
  12. import org.jboss.netty.handler.codec.http.HttpChunk;  
  13. import org.jboss.netty.handler.codec.http.HttpResponse;  
  14.   
  15. public class FileClientHandler extends SimpleChannelUpstreamHandler  
  16. {  
  17.     private volatile boolean readingChunks;  
  18.     private File downloadFile;  
  19.     private FileOutputStream fOutputStream = null;  
  20.   
  21.     @Override  
  22.     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)  
  23.             throws Exception  
  24.     {  
  25.         /* 
  26.          * 按照channle的顺序进行处理 
  27.          * server先发送HttpResponse过来,所以这里先对HttpResponse进行处理,进行文件判断之类 
  28.          * 之后,server发送的都是ChunkedFile了。 
  29.          */  
  30.           
  31.         if (e.getMessage() instanceof HttpResponse)  
  32.         {  
  33.             DefaultHttpResponse httpResponse = (DefaultHttpResponse) e  
  34.                     .getMessage();  
  35.             String fileName = httpResponse.getHeader("fileName");  
  36.             downloadFile = new File(System.getProperty("user.dir")  
  37.                     + File.separator + "recived_" + fileName);  
  38.             readingChunks = httpResponse.isChunked();  
  39.         } else  
  40.         {  
  41.             HttpChunk httpChunk = (HttpChunk) e.getMessage();  
  42.             if (!httpChunk.isLast())  
  43.             {  
  44.                 ChannelBuffer buffer = httpChunk.getContent();  
  45.                 if (fOutputStream == null)  
  46.                 {  
  47.                     fOutputStream = new FileOutputStream(downloadFile);  
  48.                 }  
  49.                 while (buffer.readable())  
  50.                 {  
  51.                     byte[] dst = new byte[buffer.readableBytes()];  
  52.                     buffer.readBytes(dst);  
  53.                     fOutputStream.write(dst);  
  54.                 }  
  55.             } else  
  56.             {  
  57.                 readingChunks = false;  
  58.             }  
  59.             fOutputStream.flush();  
  60.         }  
  61.         if (!readingChunks)  
  62.         {  
  63.             fOutputStream.close();  
  64.             e.getChannel().close();  
  65.         }  
  66.     }  
  67.   
  68.     @Override  
  69.     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)  
  70.             throws Exception  
  71.     {  
  72.         System.out.println(e.getCause());  
  73.     }  
  74. }  
package filetrans;

import java.io.File;
import java.io.FileOutputStream;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpResponse;

public class FileClientHandler extends SimpleChannelUpstreamHandler
{
    private volatile boolean readingChunks;
    private File downloadFile;
    private FileOutputStream fOutputStream = null;

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
            throws Exception
    {
        /*
         * 按照channle的顺序进行处理
         * server先发送HttpResponse过来,所以这里先对HttpResponse进行处理,进行文件判断之类
         * 之后,server发送的都是ChunkedFile了。
         */
        
        if (e.getMessage() instanceof HttpResponse)
        {
            DefaultHttpResponse httpResponse = (DefaultHttpResponse) e
                    .getMessage();
            String fileName = httpResponse.getHeader("fileName");
            downloadFile = new File(System.getProperty("user.dir")
                    + File.separator + "recived_" + fileName);
            readingChunks = httpResponse.isChunked();
        } else
        {
            HttpChunk httpChunk = (HttpChunk) e.getMessage();
            if (!httpChunk.isLast())
            {
                ChannelBuffer buffer = httpChunk.getContent();
                if (fOutputStream == null)
                {
                    fOutputStream = new FileOutputStream(downloadFile);
                }
                while (buffer.readable())
                {
                    byte[] dst = new byte[buffer.readableBytes()];
                    buffer.readBytes(dst);
                    fOutputStream.write(dst);
                }
            } else
            {
                readingChunks = false;
            }
            fOutputStream.flush();
        }
        if (!readingChunks)
        {
            fOutputStream.close();
            e.getChannel().close();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
            throws Exception
    {
        System.out.println(e.getCause());
    }
}

3、FileServer.java

  1. package filetrans;  
  2.   
  3. import static org.jboss.netty.channel.Channels.pipeline;  
  4.   
  5. import java.net.InetSocketAddress;  
  6. import java.util.concurrent.Executors;  
  7.   
  8. import org.jboss.netty.bootstrap.ServerBootstrap;  
  9. import org.jboss.netty.channel.ChannelPipeline;  
  10. import org.jboss.netty.channel.ChannelPipelineFactory;  
  11. import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;  
  12. import org.jboss.netty.handler.codec.http.HttpChunkAggregator;  
  13. import org.jboss.netty.handler.codec.http.HttpRequestDecoder;  
  14. import org.jboss.netty.handler.codec.http.HttpResponseEncoder;  
  15. import org.jboss.netty.handler.stream.ChunkedWriteHandler;  
  16.   
  17. public class FileServer  
  18. {  
  19.     public static void main(String[] args)  
  20.     {  
  21.   
  22.         ServerBootstrap bootstrap = new ServerBootstrap(  
  23.                 new NioServerSocketChannelFactory(  
  24.                         Executors.newCachedThreadPool(),  
  25.                         Executors.newCachedThreadPool()));  
  26.   
  27.         bootstrap.setPipelineFactory(new ChannelPipelineFactory()  
  28.         {  
  29.   
  30.             @Override  
  31.             public ChannelPipeline getPipeline() throws Exception  
  32.             {  
  33.                 ChannelPipeline pipeline = pipeline();  
  34.                 pipeline.addLast("decoder"new HttpRequestDecoder());  
  35.                 pipeline.addLast("aggregator"new HttpChunkAggregator(65536));  
  36.                 pipeline.addLast("encoder"new HttpResponseEncoder());  
  37.                 pipeline.addLast("chunkedWriter"new ChunkedWriteHandler());  
  38.   
  39.                 pipeline.addLast("handler"new FileServerHandler());  
  40.                 return pipeline;  
  41.             }  
  42.   
  43.         });  
  44.   
  45.         bootstrap.bind(new InetSocketAddress(8080));  
  46.     }  
  47. }  
package filetrans;

import static org.jboss.netty.channel.Channels.pipeline;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.stream.ChunkedWriteHandler;

public class FileServer
{
    public static void main(String[] args)
    {

        ServerBootstrap bootstrap = new ServerBootstrap(
                new NioServerSocketChannelFactory(
                        Executors.newCachedThreadPool(),
                        Executors.newCachedThreadPool()));

        bootstrap.setPipelineFactory(new ChannelPipelineFactory()
        {

            @Override
            public ChannelPipeline getPipeline() throws Exception
            {
                ChannelPipeline pipeline = pipeline();
                pipeline.addLast("decoder", new HttpRequestDecoder());
                pipeline.addLast("aggregator", new HttpChunkAggregator(65536));
                pipeline.addLast("encoder", new HttpResponseEncoder());
                pipeline.addLast("chunkedWriter", new ChunkedWriteHandler());

                pipeline.addLast("handler", new FileServerHandler());
                return pipeline;
            }

        });

        bootstrap.bind(new InetSocketAddress(8080));
    }
}

4、FileServerHandler.java

  1. package filetrans;  
  2.   
  3. import static org.jboss.netty.handler.codec.http.HttpHeaders.*;  
  4. import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;  
  5. import static org.jboss.netty.handler.codec.http.HttpMethod.*;  
  6. import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;  
  7. import static org.jboss.netty.handler.codec.http.HttpVersion.*;  
  8.   
  9. import java.io.File;  
  10. import java.io.FileNotFoundException;  
  11. import java.io.RandomAccessFile;  
  12. import java.io.UnsupportedEncodingException;  
  13. import java.net.URLDecoder;  
  14.   
  15. import org.jboss.netty.buffer.ChannelBuffers;  
  16. import org.jboss.netty.channel.Channel;  
  17. import org.jboss.netty.channel.ChannelFuture;  
  18. import org.jboss.netty.channel.ChannelFutureListener;  
  19. import org.jboss.netty.channel.ChannelFutureProgressListener;  
  20. import org.jboss.netty.channel.ChannelHandlerContext;  
  21. import org.jboss.netty.channel.DefaultFileRegion;  
  22. import org.jboss.netty.channel.ExceptionEvent;  
  23. import org.jboss.netty.channel.FileRegion;  
  24. import org.jboss.netty.channel.MessageEvent;  
  25. import org.jboss.netty.channel.SimpleChannelUpstreamHandler;  
  26. import org.jboss.netty.handler.codec.frame.TooLongFrameException;  
  27. import org.jboss.netty.handler.codec.http.DefaultHttpResponse;  
  28. import org.jboss.netty.handler.codec.http.HttpRequest;  
  29. import org.jboss.netty.handler.codec.http.HttpResponse;  
  30. import org.jboss.netty.handler.codec.http.HttpResponseStatus;  
  31. import org.jboss.netty.handler.ssl.SslHandler;  
  32. import org.jboss.netty.handler.stream.ChunkedFile;  
  33. import org.jboss.netty.util.CharsetUtil;  
  34.   
  35. /** 
  36.  * 这里的代码主要来源于官网的例子,http里面有个例子,自己仿照server写了client 
  37.  * @author Ransom 
  38.  */  
  39. public class FileServerHandler extends SimpleChannelUpstreamHandler  
  40. {  
  41.   
  42.     @Override  
  43.     public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)  
  44.             throws Exception  
  45.     {  
  46.         HttpRequest request = (HttpRequest) e.getMessage();  
  47.         if (request.getMethod() != GET)  
  48.         {  
  49.             sendError(ctx, METHOD_NOT_ALLOWED);  
  50.             return;  
  51.         }  
  52.   
  53.         final String path = sanitizeUri(request.getUri());  
  54.         if (path == null)  
  55.         {  
  56.             sendError(ctx, FORBIDDEN);  
  57.             return;  
  58.         }  
  59.   
  60.         File file = new File(path);  
  61.         if (file.isHidden() || !file.exists())  
  62.         {  
  63.             sendError(ctx, NOT_FOUND);  
  64.             return;  
  65.         }  
  66.         if (!file.isFile())  
  67.         {  
  68.             sendError(ctx, FORBIDDEN);  
  69.             return;  
  70.         }  
  71.   
  72.         RandomAccessFile raf;  
  73.         try  
  74.         {  
  75.             raf = new RandomAccessFile(file, "r");  
  76.         } catch (FileNotFoundException fnfe)  
  77.         {  
  78.             sendError(ctx, NOT_FOUND);  
  79.             return;  
  80.         }  
  81.         long fileLength = raf.length();  
  82.   
  83.         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);  
  84.           
  85.         /* 
  86.          * 由于是异步传输,所以不得已加入了一些属性,用来进行文件识别 
  87.          */  
  88.         response.addHeader("fileName", request.getUri());  
  89.           
  90.         setContentLength(response, fileLength);  
  91.   
  92.         Channel ch = e.getChannel();  
  93.   
  94.         // Write the initial line and the header.  
  95.         ch.write(response);  
  96.   
  97.         // Write the content.  
  98.         ChannelFuture writeFuture;  
  99.         if (ch.getPipeline().get(SslHandler.class) != null)  
  100.         {  
  101.             // Cannot use zero-copy with HTTPS.  
  102.             writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));  
  103.         } else  
  104.         {  
  105.             // No encryption - use zero-copy.  
  106.             final FileRegion region = new DefaultFileRegion(raf.getChannel(),  
  107.                     0, fileLength);  
  108.             writeFuture = ch.write(region);  
  109.             writeFuture.addListener(new ChannelFutureProgressListener()  
  110.             {  
  111.                 public void operationComplete(ChannelFuture future)  
  112.                 {  
  113.                     region.releaseExternalResources();  
  114.                 }  
  115.   
  116.                 public void operationProgressed(ChannelFuture future,  
  117.                         long amount, long current, long total)  
  118.                 {  
  119.                     System.out.printf("%s: %d / %d (+%d)%n", path, current,  
  120.                             total, amount);  
  121.                 }  
  122.             });  
  123.         }  
  124.   
  125.         // Decide whether to close the connection or not.  
  126.         if (!isKeepAlive(request))  
  127.         {  
  128.             // Close the connection when the whole content is written out.  
  129.             writeFuture.addListener(ChannelFutureListener.CLOSE);  
  130.         }  
  131.     }  
  132.   
  133.     @Override  
  134.     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)  
  135.             throws Exception  
  136.     {  
  137.         Channel ch = e.getChannel();  
  138.         Throwable cause = e.getCause();  
  139.         if (cause instanceof TooLongFrameException)  
  140.         {  
  141.             sendError(ctx, BAD_REQUEST);  
  142.             return;  
  143.         }  
  144.   
  145.         cause.printStackTrace();  
  146.         if (ch.isConnected())  
  147.         {  
  148.             sendError(ctx, INTERNAL_SERVER_ERROR);  
  149.         }  
  150.     }  
  151.   
  152.     private String sanitizeUri(String uri)  
  153.     {  
  154.         // Decode the path.  
  155.         try  
  156.         {  
  157.             uri = URLDecoder.decode(uri, "UTF-8");  
  158.         } catch (UnsupportedEncodingException e)  
  159.         {  
  160.             try  
  161.             {  
  162.                 uri = URLDecoder.decode(uri, "ISO-8859-1");  
  163.             } catch (UnsupportedEncodingException e1)  
  164.             {  
  165.                 throw new Error();  
  166.             }  
  167.         }  
  168.   
  169.         // Convert file separators.  
  170.         uri = uri.replace('/', File.separatorChar);  
  171.   
  172.         // Simplistic dumb security check.  
  173.         // You will have to do something serious in the production environment.  
  174.         if (uri.contains(File.separator + ".")  
  175.                 || uri.contains("." + File.separator) || uri.startsWith(".")  
  176.                 || uri.endsWith("."))  
  177.         {  
  178.             return null;  
  179.         }  
  180.   
  181.         // Convert to absolute path.  
  182.         return System.getProperty("user.dir") + File.separator + uri;  
  183.     }  
  184.   
  185.     private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status)  
  186.     {  
  187.         HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);  
  188.         response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");  
  189.         response.setContent(ChannelBuffers.copiedBuffer(  
  190.                 "Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8));  
  191.   
  192.         // Close the connection as soon as the error message is sent.  
  193.         ctx.getChannel().write(response)  
  194.                 .addListener(ChannelFutureListener.CLOSE);  
  195.     }  
  196. }  
package filetrans;

import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
import static org.jboss.netty.handler.codec.http.HttpMethod.*;
import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
import static org.jboss.netty.handler.codec.http.HttpVersion.*;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelFutureProgressListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.DefaultFileRegion;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.FileRegion;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.frame.TooLongFrameException;
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.ssl.SslHandler;
import org.jboss.netty.handler.stream.ChunkedFile;
import org.jboss.netty.util.CharsetUtil;

/**
 * 这里的代码主要来源于官网的例子,http里面有个例子,自己仿照server写了client
 * @author Ransom
 */
public class FileServerHandler extends SimpleChannelUpstreamHandler
{

    @Override
    public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
            throws Exception
    {
        HttpRequest request = (HttpRequest) e.getMessage();
        if (request.getMethod() != GET)
        {
            sendError(ctx, METHOD_NOT_ALLOWED);
            return;
        }

        final String path = sanitizeUri(request.getUri());
        if (path == null)
        {
            sendError(ctx, FORBIDDEN);
            return;
        }

        File file = new File(path);
        if (file.isHidden() || !file.exists())
        {
            sendError(ctx, NOT_FOUND);
            return;
        }
        if (!file.isFile())
        {
            sendError(ctx, FORBIDDEN);
            return;
        }

        RandomAccessFile raf;
        try
        {
            raf = new RandomAccessFile(file, "r");
        } catch (FileNotFoundException fnfe)
        {
            sendError(ctx, NOT_FOUND);
            return;
        }
        long fileLength = raf.length();

        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
        
        /*
         * 由于是异步传输,所以不得已加入了一些属性,用来进行文件识别
         */
        response.addHeader("fileName", request.getUri());
        
        setContentLength(response, fileLength);

        Channel ch = e.getChannel();

        // Write the initial line and the header.
        ch.write(response);

        // Write the content.
        ChannelFuture writeFuture;
        if (ch.getPipeline().get(SslHandler.class) != null)
        {
            // Cannot use zero-copy with HTTPS.
            writeFuture = ch.write(new ChunkedFile(raf, 0, fileLength, 8192));
        } else
        {
            // No encryption - use zero-copy.
            final FileRegion region = new DefaultFileRegion(raf.getChannel(),
                    0, fileLength);
            writeFuture = ch.write(region);
            writeFuture.addListener(new ChannelFutureProgressListener()
            {
                public void operationComplete(ChannelFuture future)
                {
                    region.releaseExternalResources();
                }

                public void operationProgressed(ChannelFuture future,
                        long amount, long current, long total)
                {
                    System.out.printf("%s: %d / %d (+%d)%n", path, current,
                            total, amount);
                }
            });
        }

        // Decide whether to close the connection or not.
        if (!isKeepAlive(request))
        {
            // Close the connection when the whole content is written out.
            writeFuture.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
            throws Exception
    {
        Channel ch = e.getChannel();
        Throwable cause = e.getCause();
        if (cause instanceof TooLongFrameException)
        {
            sendError(ctx, BAD_REQUEST);
            return;
        }

        cause.printStackTrace();
        if (ch.isConnected())
        {
            sendError(ctx, INTERNAL_SERVER_ERROR);
        }
    }

    private String sanitizeUri(String uri)
    {
        // Decode the path.
        try
        {
            uri = URLDecoder.decode(uri, "UTF-8");
        } catch (UnsupportedEncodingException e)
        {
            try
            {
                uri = URLDecoder.decode(uri, "ISO-8859-1");
            } catch (UnsupportedEncodingException e1)
            {
                throw new Error();
            }
        }

        // Convert file separators.
        uri = uri.replace('/', File.separatorChar);

        // Simplistic dumb security check.
        // You will have to do something serious in the production environment.
        if (uri.contains(File.separator + ".")
                || uri.contains("." + File.separator) || uri.startsWith(".")
                || uri.endsWith("."))
        {
            return null;
        }

        // Convert to absolute path.
        return System.getProperty("user.dir") + File.separator + uri;
    }

    private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status)
    {
        HttpResponse response = new DefaultHttpResponse(HTTP_1_1, status);
        response.setHeader(CONTENT_TYPE, "text/plain; charset=UTF-8");
        response.setContent(ChannelBuffers.copiedBuffer(
                "Failure: " + status.toString() + "\r\n", CharsetUtil.UTF_8));

        // Close the connection as soon as the error message is sent.
        ctx.getChannel().write(response)
                .addListener(ChannelFutureListener.CLOSE);
    }
}

Netty的例子到这里就算贴完了,后面还打算进行一些Netty实现原理,架构,代码解读方面的分析。到时候如果有什么心得,再写出来吧。

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值