• netty 下载文件


    需求:

      基于netty框架完成一个文件服务器,通过浏览器可以浏览相关的文件及下载文件。

    目录结构

      在跟src同级别目录下,放一个sources 文件夹,并向文件夹内放入一些文件,用于测试下载。

      

    代码:

      代码分为两个,一个是 HttpFileServer 用于做通道连接。另外一个是 HttpFileServerHandler 用于具体的业务处理。  

     1 import io.netty.bootstrap.ServerBootstrap;
     2 import io.netty.channel.ChannelFuture;
     3 import io.netty.channel.ChannelInitializer;
     4 import io.netty.channel.EventLoopGroup;
     5 import io.netty.channel.nio.NioEventLoopGroup;
     6 import io.netty.channel.socket.SocketChannel;
     7 import io.netty.channel.socket.nio.NioServerSocketChannel;
     8 import io.netty.handler.codec.http.HttpObjectAggregator;
     9 import io.netty.handler.codec.http.HttpRequestDecoder;
    10 import io.netty.handler.codec.http.HttpResponseEncoder;
    11 import io.netty.handler.stream.ChunkedWriteHandler;
    12 
    13 public class HttpFileServer {
    14 
    15     private static final String DEFAULT_URL="/sources/";
    16 
    17     public void run (final int port,final String url) throws Exception {
    18         EventLoopGroup bossGroup = new NioEventLoopGroup();
    19         EventLoopGroup workerGroup = new NioEventLoopGroup();
    20 
    21         try {
    22             ServerBootstrap b =new ServerBootstrap();
    23             b.group(bossGroup,workerGroup)
    24                     .channel(NioServerSocketChannel.class)
    25                     .childHandler(new ChannelInitializer<SocketChannel>() {
    26                         @Override
    27                         protected void initChannel(SocketChannel ch) throws Exception {
    28                             ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());// 解码器
    29                             ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));
    30                             ch.pipeline().addLast("http-encoder",new HttpResponseEncoder());// 编码器
    31                             ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());// 压缩解压
    32                             ch.pipeline().addLast("fileServerHandler",new HttpFileServerHandler(url));// 实际的文件下载处理handler
    33                         }
    34                     });
    35             ChannelFuture future = b.bind(port).sync();
    36             System.out.println("运行在端口号 port = " + port);
    37             future.channel().closeFuture().sync();
    38         }finally {
    39             bossGroup.shutdownGracefully();
    40             workerGroup.shutdownGracefully();
    41         }
    42 
    43     }
    44 
    45     public static void main(String[] args) throws  Exception{
    46         int port = 8765;
    47         String url = DEFAULT_URL;
    48         new HttpFileServer().run(port,url);
    49     }
    50 
    51 }
    HttpFileServer
      1 import java.io.File;
      2 import java.io.FileNotFoundException;
      3 import java.io.RandomAccessFile;
      4 import java.io.UnsupportedEncodingException;
      5 import java.net.URLDecoder;
      6 import java.util.concurrent.ExecutionException;
      7 import java.util.concurrent.TimeUnit;
      8 import java.util.concurrent.TimeoutException;
      9 import java.util.regex.Pattern;
     10 
     11 import javax.activation.MimetypesFileTypeMap;
     12 import javax.swing.text.html.MinimalHTMLWriter;
     13 
     14 import io.netty.buffer.ByteBuf;
     15 import io.netty.buffer.Unpooled;
     16 import io.netty.channel.Channel;
     17 import io.netty.channel.ChannelFuture;
     18 import io.netty.channel.ChannelFutureListener;
     19 import io.netty.channel.ChannelHandlerContext;
     20 import io.netty.channel.ChannelProgressiveFuture;
     21 import io.netty.channel.ChannelProgressiveFutureListener;
     22 import io.netty.channel.SimpleChannelInboundHandler;
     23 import io.netty.handler.codec.http.DefaultFullHttpResponse;
     24 import io.netty.handler.codec.http.DefaultHttpResponse;
     25 import io.netty.handler.codec.http.FullHttpRequest;
     26 import io.netty.handler.codec.http.FullHttpResponse;
     27 import io.netty.handler.codec.http.HttpHeaderNames;
     28 import io.netty.handler.codec.http.HttpHeaderUtil;
     29 import io.netty.handler.codec.http.HttpHeaderValues;
     30 import io.netty.handler.codec.http.HttpHeaders;
     31 import io.netty.handler.codec.http.HttpMessage;
     32 import io.netty.handler.codec.http.HttpMethod;
     33 import io.netty.handler.codec.http.HttpResponse;
     34 import io.netty.handler.codec.http.HttpResponseStatus;
     35 import io.netty.handler.codec.http.HttpVersion;
     36 import io.netty.handler.codec.http.LastHttpContent;
     37 import io.netty.handler.codec.http2.Http2Headers;
     38 import io.netty.handler.stream.ChunkedFile;
     39 import io.netty.util.CharsetUtil;
     40 import io.netty.util.concurrent.Future;
     41 import io.netty.util.concurrent.GenericFutureListener;
     42 
     43 public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest>{
     44 
     45     private final String url;
     46 
     47     public HttpFileServerHandler(String url) {
     48         this.url = url;
     49     }
     50 
     51     @Override
     52     protected void messageReceived(ChannelHandlerContext ctx,
     53                                    FullHttpRequest request) throws Exception {
     54         // 对请求的解码结果进行判断
     55         if(!request.decoderResult().isSuccess())
     56         {
     57             // 400
     58             sendError(ctx, HttpResponseStatus.BAD_REQUEST);
     59             return;
     60         }
     61         // 对请求方式进行判断,如果不是get方式(如post方式)则返回异常
     62         if(request.method() != HttpMethod.GET)
     63         {
     64             // 405
     65             sendError(ctx, HttpResponseStatus.METHOD_NOT_ALLOWED);
     66             return;
     67         }
     68 
     69         final String uri = request.uri();
     70         final String path = sanitizeUri(uri);
     71         if(path == null)
     72         {
     73             // 403
     74             sendError(ctx, HttpResponseStatus.FORBIDDEN);
     75             return;
     76         }
     77 
     78         File file = new File(path);
     79         if(file.isHidden() || !file.exists())
     80         {
     81             // 404
     82             sendError(ctx, HttpResponseStatus.NOT_FOUND);
     83             return;
     84         }
     85         if(file.isDirectory())
     86         {
     87             if(uri.endsWith("/"))
     88             {
     89                 sendListing(ctx, file);
     90             }else{
     91                 sendRedirect(ctx, uri + "/");
     92             }
     93             return;
     94         }
     95         if(!file.isFile())
     96         {
     97             // 403
     98             sendError(ctx, HttpResponseStatus.FORBIDDEN);
     99             return;
    100         }
    101 
    102         RandomAccessFile randomAccessFile = null;
    103         try{
    104             randomAccessFile = new RandomAccessFile(file, "r");
    105         }catch(FileNotFoundException fnfd){
    106             sendError(ctx, HttpResponseStatus.NOT_FOUND);
    107             return;
    108         }
    109 
    110         long fileLength = randomAccessFile.length();
    111         HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
    112         HttpHeaderUtil.setContentLength(response, fileLength);
    113 //        setContentLength(response, fileLength);
    114         setContentTypeHeader(response, file);
    115 
    116 
    117 
    118         if(HttpHeaderUtil.isKeepAlive(request)){
    119             response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
    120         }
    121 
    122         ctx.write(response);
    123         ChannelFuture sendFileFuture = null;
    124         sendFileFuture = ctx.write(new ChunkedFile(randomAccessFile, 0, fileLength, 8192), ctx.newProgressivePromise());
    125         sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
    126 
    127             @Override
    128             public void operationComplete(ChannelProgressiveFuture future)
    129                     throws Exception {
    130                 System.out.println("Transfer complete.");
    131 
    132             }
    133 
    134             @Override
    135             public void operationProgressed(ChannelProgressiveFuture future,
    136                                             long progress, long total) throws Exception {
    137                 if(total < 0)
    138                     System.err.println("Transfer progress: " + progress);
    139                 else
    140                     System.err.println("Transfer progress: " + progress + "/" + total);
    141             }
    142         });
    143 
    144         ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    145         if(!HttpHeaderUtil.isKeepAlive(request))
    146             lastContentFuture.addListener(ChannelFutureListener.CLOSE);
    147 
    148     }
    149 
    150     @Override
    151     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    152             throws Exception {
    153         cause.printStackTrace();
    154         if(ctx.channel().isActive())
    155             sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
    156     }
    157 
    158     private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&"].*");
    159     private String sanitizeUri(String uri){
    160         try{
    161             uri = URLDecoder.decode(uri, "UTF-8");
    162         }catch(UnsupportedEncodingException e){
    163             try{
    164                 uri = URLDecoder.decode(uri, "ISO-8859-1");
    165             }catch(UnsupportedEncodingException e1){
    166                 throw new Error();
    167             }
    168         }
    169 
    170         if(!uri.startsWith(url))
    171             return null;
    172         if(!uri.startsWith("/"))
    173             return null;
    174 
    175         uri = uri.replace('/', File.separatorChar);
    176         if(uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.startsWith(".") || uri.endsWith(".")
    177                 || INSECURE_URI.matcher(uri).matches()){
    178             return null;
    179         }
    180         return System.getProperty("user.dir") + File.separator + uri;
    181     }
    182 
    183     private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\.]*");
    184 
    185     private static void sendListing(ChannelHandlerContext ctx, File dir){
    186         FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
    187 //        response.headers().set("CONNECT_TYPE", "text/html;charset=UTF-8");
    188         response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
    189 
    190         String dirPath = dir.getPath();
    191         StringBuilder buf = new StringBuilder();
    192 
    193         buf.append("<!DOCTYPE html>
    ");
    194         buf.append("<html><head><title>");
    195         buf.append(dirPath);
    196         buf.append("目录:");
    197         buf.append("</title></head><body>
    ");
    198 
    199         buf.append("<h3>");
    200         buf.append(dirPath).append(" 目录:");
    201         buf.append("</h3>
    ");
    202         buf.append("<ul>");
    203         buf.append("<li>链接:<a href=" ../")..</a></li>
    ");
    204         for (File f : dir.listFiles()) {
    205             if(f.isHidden() || !f.canRead()) {
    206                 continue;
    207             }
    208             String name = f.getName();
    209             if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
    210                 continue;
    211             }
    212 
    213             buf.append("<li>链接:<a href="");
    214             buf.append(name);
    215             buf.append("">");
    216             buf.append(name);
    217             buf.append("</a></li>
    ");
    218         }
    219 
    220         buf.append("</ul></body></html>
    ");
    221 
    222         ByteBuf buffer = Unpooled.copiedBuffer(buf,CharsetUtil.UTF_8);
    223         response.content().writeBytes(buffer);
    224         buffer.release();
    225         ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    226     }
    227 
    228 
    229     private static void sendRedirect(ChannelHandlerContext ctx, String newUri){
    230         FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.FOUND);
    231 //        response.headers().set("LOCATIN", newUri);
    232         response.headers().set(HttpHeaderNames.LOCATION, newUri);
    233         ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    234     }
    235     private static void sendError(ChannelHandlerContext ctx, HttpResponseStatus status){
    236         FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status,
    237                 Unpooled.copiedBuffer("Failure: " + status.toString() + "
    ", CharsetUtil.UTF_8));
    238         response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
    239         ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    240     }
    241     private static void setContentTypeHeader(HttpResponse response, File file){
    242         MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();
    243         response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimetypesFileTypeMap.getContentType(file.getPath()));
    244     }
    245 }
    HttpFileServerHandler

    页面效果:

  • 相关阅读:
    js 各种常用js验证
    js url校验
    最近遇到的技术问题
    a标签的target的四个值
    新系统用到的新知识
    7 天打造前端性能监控系统
    前端必读:浏览器内部工作原理
    怎么判断ThreadPool线程池里的任务都执行完毕
    docker 在window 10 专业版的安装 && .net core 在docker的部署
    .net core 中后台获取前台 数据(post)的方法
  • 原文地址:https://www.cnblogs.com/liyasong/p/File.html
Copyright © 2020-2023  润新知