• Netty+SpringBoot写一个基于Http协议的文件服务器


    本文参考《Netty权威指南》

    NettyApplication
    package com.xh.netty;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class NettyApplication {
    
       public static void main(String[] args) {
    
          SpringApplication.run(NettyApplication.class, args);
    
          String[] argList =args;
          System.out.println("+++++++++++++Simple Netty HttpFileServer+++++++++++++++");
          System.out.println("+            VERSION 1.0.1                            +");
          System.out.println("+            AUTHER:XH                                +");
          System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++");
          if (args.length==0){
             System.out.println("Usage: java -var thisPackageName.jar [-options][args...]");
             System.out.println("Use -h for more infomation");
             System.out.println("default port is 8080 , webRoot is /root ");
          }else {
             for (int i=0;i<argList.length;i++){
                if (argList[i].equalsIgnoreCase("-h")){
                   System.out.println("-p your Listern port");
                   System.out.println("-f your webRoot path");
                   System.out.println("Example:java -jar netty-0.0.1-SNAPSHOT.jar -p 80 -f /root");
                   return;
                }else {
                   if (argList[i].equalsIgnoreCase("-p")){
                      try{
                         HttpFileServer.PORT=Integer.valueOf(argList[i+1]);
                      }catch (NumberFormatException e){
                         System.out.println("wrong number for you port");
                         System.out.println("Use -h for more infomation");
                         e.printStackTrace();
                         return;
                      }
                   }
                   if (argList[i].equalsIgnoreCase("-f")){
                      try{
                         HttpFileServer.WEBROOT=argList[i+1];
                      }catch (Exception e){
                         System.out.println("wrong path for you webRoot");
                         System.out.println("Use -h for more infomation");
                         e.printStackTrace();
                         return;
                      }
                   }
                }
             }
          }
    
          try {
             HttpFileServer.main();
          } catch (InterruptedException e) {
             e.printStackTrace();
          }
    
       }
    }
    
    HttpFileServer
    package com.xh.netty;
    
    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import io.netty.handler.codec.http.HttpObjectAggregator;
    import io.netty.handler.codec.http.HttpRequestDecoder;
    import io.netty.handler.codec.http.HttpResponseEncoder;
    import io.netty.handler.stream.ChunkedWriteHandler;
    
    /**
     * Created by root on 8/14/17.
     */
    public class HttpFileServer {
    
        public static  String WEBROOT = "/root";
        public static  int PORT = 8080;
    
        public void run(final int port ,  final String url) throws InterruptedException {
            EventLoopGroup bossGroup=new NioEventLoopGroup();
            EventLoopGroup workerGroup=new NioEventLoopGroup();
            try{
                ServerBootstrap bootstrap=new ServerBootstrap();
                bootstrap.group(bossGroup,workerGroup)
                        .channel(NioServerSocketChannel.class)
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                socketChannel.pipeline().addLast("http-decoder",new HttpRequestDecoder());
                                socketChannel.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));
                                socketChannel.pipeline().addLast("http-encoder",new HttpResponseEncoder());
                                socketChannel.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
                                socketChannel.pipeline().addLast("fileServerHandler",new HttpFileServerHandler(url));
    
                            }
                        });
                ChannelFuture future = bootstrap.bind("127.0.0.1",port).sync();
                System.out.println("服务器已启动>>网址:"+"127.0.0.1:"+port+url);
                future.channel().closeFuture().sync();
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
    
        }
    
        public static void main() throws InterruptedException {
    
            new HttpFileServer().run(PORT,WEBROOT);
        }
    }
    
    HttpFileServerHandler
    package com.xh.netty;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.*;
    import io.netty.handler.codec.http.*;
    import io.netty.handler.codec.http.HttpResponseStatus;
    import io.netty.handler.stream.ChunkedFile;
    import io.netty.util.CharsetUtil;
    
    import javax.activation.MimetypesFileTypeMap;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.RandomAccessFile;
    import java.net.URLDecoder;
    import java.util.regex.Pattern;
    
    import static io.netty.handler.codec.http.HttpHeaderNames.*;
    import static io.netty.handler.codec.http.HttpHeaderUtil.isKeepAlive;
    import static io.netty.handler.codec.http.HttpHeaderUtil.setContentLength;
    import static io.netty.handler.codec.http.HttpHeaderValues.KEEP_ALIVE;
    import static io.netty.handler.codec.http.HttpResponseStatus.*;
    import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
    
    /**
     * Created by root on 8/14/17.
     */
    public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest>{
    
        private final String url;
        String WEBROOT = HttpFileServer.WEBROOT;
        public HttpFileServerHandler(String url) {
            this.url = url;
        }
    
        protected void messageReceived(ChannelHandlerContext channelHandlerContext, FullHttpRequest fullHttpRequest) throws Exception {
            if (!fullHttpRequest.decoderResult().isSuccess()){
                sendError(channelHandlerContext, HttpResponseStatus.BAD_REQUEST);
                return;
            }
    
            if (fullHttpRequest.method()!= HttpMethod.GET){
                sendError(channelHandlerContext,HttpResponseStatus.METHOD_NOT_ALLOWED);
                return;
            }
    
            String uri=fullHttpRequest.uri();
            if (uri==null||uri.trim().equalsIgnoreCase("")){
                uri="/";
            }
            if (uri.trim().equalsIgnoreCase("/")){
                uri= WEBROOT;
            }
            if(!uri.startsWith(WEBROOT)){
                uri= WEBROOT +uri;
            }
            final String path=sanitizeUri(uri);
            if (path==null){
                sendError(channelHandlerContext,HttpResponseStatus.FORBIDDEN);
                return;
            }
    
            File file=new File(path);
            if (file.isHidden()||!file.exists()){
                sendError(channelHandlerContext,HttpResponseStatus.NOT_FOUND);
                return;
            }
    
            if (file.isDirectory()){
                if (uri.endsWith("/")){
                    senfListing(channelHandlerContext,file);
    
                }else {
                    sendRedirect(channelHandlerContext,uri+"/");
                }
    
                return;
            }
    
            if (!file.isFile()){
                sendError(channelHandlerContext,HttpResponseStatus.FORBIDDEN);
                return;
            }
    
            RandomAccessFile randomAccessFile=null;
            try{
                randomAccessFile=new RandomAccessFile(file,"r");
            }catch (FileNotFoundException e){
                e.printStackTrace();
                sendError(channelHandlerContext,HttpResponseStatus.NOT_FOUND);
                return;
            }
    
            Long fileLength=randomAccessFile.length();
            HttpResponse httpResponse=new DefaultHttpResponse(HTTP_1_1,OK);
            setContentLength(httpResponse,fileLength);
            setContentTypeHeader(httpResponse,file);
    
            if (isKeepAlive(fullHttpRequest)){
                httpResponse.headers().set(CONNECTION,KEEP_ALIVE);
            }
    
            channelHandlerContext.writeAndFlush(httpResponse);
            ChannelFuture sendFileFuture = channelHandlerContext.write(
                    new ChunkedFile(randomAccessFile,0,fileLength,8192),channelHandlerContext.newProgressivePromise());
    
            sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) {
                    if (total<0){
                        System.err.println("progress:"+progress);
                    }else {
                        System.err.println("progress:"+progress+"/"+total);
                    }
                }
    
                public void operationComplete(ChannelProgressiveFuture future) {
                    System.err.println("complete");
                }
            });
    
    
            ChannelFuture lastChannelFuture=channelHandlerContext.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
            if (!isKeepAlive(fullHttpRequest)){
                lastChannelFuture.addListener(ChannelFutureListener.CLOSE );
    
            }
    
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            if (ctx.channel().isActive()){
                sendError(ctx,INTERNAL_SERVER_ERROR);
            }
        }
    
    
    
        private static final Pattern INSECURE_URI=Pattern.compile(".*[<>&"].*");
    
        public String sanitizeUri(String uri){
            try{
                uri= URLDecoder.decode(uri,"UTF-8");
            }catch (Exception e){
                try{
                    uri= URLDecoder.decode(uri,"ISO-8859-1");
                }catch (Exception ew){
                    ew.printStackTrace();
                }
            }
    
            if (!uri.startsWith(url)){
                return null;
    
            }
    
            if (!uri.startsWith("/")){
                return null;
    
            }
    
            uri=uri.replace('/',File.separatorChar);
            if (uri.contains(File.separator+'.')||uri.startsWith(".")||uri.endsWith(".")||INSECURE_URI.matcher(uri).matches()){
                return null;
            }
    
            return uri;//System.getProperty("user.dir")+uri
    
        }
    
        private static final Pattern ALLOWED_FILE_NAME=Pattern.compile("[a-zA-Z0-9\.]*");
        private void senfListing(ChannelHandlerContext channelHandlerContext, File dir) {
            FullHttpResponse response=new DefaultFullHttpResponse(HTTP_1_1,OK);
            response.headers().set(CONTENT_TYPE,"text/html;charset=UTF-8");
            StringBuilder builder =new StringBuilder();
            String dirPath=dir.getPath();
            builder.append("<!DOCTYPE html> 
    ");
            builder.append("<html><head><title>");
            builder.append(dirPath);
            builder.append("目录:");
            builder.append("</title></head><body>
    ");
            builder.append("<h3>");
            builder.append(dirPath).append("目录:");
            builder.append("</h3>
    ");
            builder.append("<ul>");
            builder.append("<li>链接:<a href="../">..</a></li>
    ");
            for (File f:dir.listFiles()){
                if (f.isHidden()||!f.canRead()){
                    continue;
                }
                String fname=f.getName();
                if (!ALLOWED_FILE_NAME.matcher(fname).matches()){
                    continue;
                }
                builder.append("<li>链接:<a href=" ");
                builder.append(fname);
                builder.append("" >");
                builder.append(fname);
                builder.append("</a></li>
    ");
            }
            builder.append("</ul></body></html>
    ");
    
            ByteBuf byteBuf= Unpooled.copiedBuffer(builder, CharsetUtil.UTF_8);
            response.content().writeBytes(byteBuf);
            byteBuf.release();
            channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    
        }
    
    
        private void sendRedirect(ChannelHandlerContext channelHandlerContext, String newUri) {
            FullHttpResponse response=new DefaultFullHttpResponse(HTTP_1_1,FOUND);
            response.headers().set(LOCATION,newUri);
            channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
        }
    
        private void sendError(ChannelHandlerContext channelHandlerContext, HttpResponseStatus status) {
            FullHttpResponse response=new DefaultFullHttpResponse(
                    HTTP_1_1,status,Unpooled.copiedBuffer("Failure: "+status.toString()+"
    ",
                    CharsetUtil.UTF_8));
            response.headers().set(CONTENT_TYPE,"text/plain; charset=UTF-8");
            channelHandlerContext.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    
        }
    
    
        private void setContentTypeHeader(HttpResponse httpResponse, File file) {
    
            MimetypesFileTypeMap mimetypesFileTypeMap=new MimetypesFileTypeMap();
            httpResponse.headers().set(CONTENT_TYPE,mimetypesFileTypeMap.getContentType(file.getPath()));
        }
    
    
    
    }
    

    打包发布:

    cd 到项目target同级目录

    mvn clean package

    然后 cd target/

     java -jar netty-0.0.1-SNAPSHOT.jar -h运行

  • 相关阅读:
    WPF 制作高性能的透明背景异形窗口(使用 WindowChrome 而不要使用 AllowsTransparency=True)
    【ASP.NET Core】自己编程来生成自签名的服务器证书
    WPF:DataGrid可过滤、多语言
    将python脚本打包为exe可执行文件
    C# WPF DATAGRID 分组(GROUP)
    皮肤样式备份
    wpf – 如何在UIElement.Margin上为绑定设置FallbackValue?
    Wpf虚拟屏幕键盘
    ubuntu docker 安装 redis
    fastapi 返回请求头信息
  • 原文地址:https://www.cnblogs.com/lanqie/p/7638431.html
Copyright © 2020-2023  润新知