Netty实现HTTP文件服务器

Netty实现HTTP文件服务器

1. 实现步骤

  • Netty服务器启动
  • 文件服务handler实现
  • 添加通道处理器

2. 具体实现

2.1 Netty服务器启动

  • netty的服务启动基本类似
public class MyStaticWebServer {
     

    private ServerBootstrap serverBootstrap;
    private Integer port;

    public MyStaticWebServer(Integer port)  {
     
        this.port = port;
        serverBootstrap = new ServerBootstrap();
    }

    public MyStaticWebServer() {
     
        this(8888);
    }

    public void start() {
     
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup work = new NioEventLoopGroup();

        try {
     
            serverBootstrap.group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(port)
                    .childHandler(new MyStaticWebInitializer());

            ChannelFuture bindFuture = serverBootstrap.bind().sync();
            ChannelFuture sync = bindFuture.channel().closeFuture().sync();
        } catch (Exception e) {
     
            e.printStackTrace();
        }
    }

}

2.2 文件服务handler实现

public class MyFileHandler extends SimpleChannelInboundHandler<HttpObject> {
     


    private String basePath = "D:\\";

    protected void channelRead0(
            ChannelHandlerContext ctx, HttpObject httpObject) {
     
        HttpRequest request = (HttpRequest) httpObject;
        String uri = request.getUri();
        // 默认过滤处理
        if (defaultFilterRequest(uri)) {
     
            sendError(ctx);
            return;
        }
        // 处理文件发送
        try {
     
            String fileName = uri.substring(uri.lastIndexOf("/"));
            sendFile2Browser(fileName, ctx);
        } catch (Exception e) {
     
            e.printStackTrace();
            sendError(ctx);
        }
    }

    /**
     * 发送数据到浏览器
     * @param fileName
     * @param ctx
     * @throws Exception
     */
    private void sendFile2Browser(String fileName, ChannelHandlerContext ctx) throws Exception {
     
        // 获取文件输入流
        RandomAccessFile randomAccessFile = new RandomAccessFile(basePath + fileName, "r");
        long fileLength = randomAccessFile.length();
        // 封装响应体
        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, fileLength);
        // 动态的根据文件名获取媒体类型
        MimetypesFileTypeMap mimetypesFileTypeMap = new MimetypesFileTypeMap();
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimetypesFileTypeMap.getContentType(fileName));
        ctx.write(response);
        //真正写数据
        ChannelFuture sendFileFuture = null;
        //每一块儿文件的大小
        ChunkedFile chunkedFile = new ChunkedFile(randomAccessFile, 0, fileLength, 8192);
        sendFileFuture = ctx.write(chunkedFile, ctx.newProgressivePromise());
        // 添加文件传输进度监控可有可无
        sendFileFuture.addListener(new MyChannelProgressiveFutureListener());
        // 响应结尾数据
        ChannelFuture lastContentFuture = ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    }

    /**
     * 向前端发送404错误
     * @param ctx
     */
    private void sendError(ChannelHandlerContext ctx) {
     
        ByteBuf byteBuf = Unpooled.copiedBuffer("Failure: Not Found \r\n", CharsetUtil.UTF_8);
        HttpResponse response = new DefaultFullHttpResponse(
                HttpVersion.HTTP_1_1,
                HttpResponseStatus.NOT_FOUND,
                byteBuf
        );
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, byteBuf.readableBytes());
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=UTF-8");
        ctx.writeAndFlush(response);

    }

    /**
     * 判断当前请求是否需要过滤
     * @param uri 请求uri
     * @return
     */
    private boolean defaultFilterRequest(String uri) {
     
        if (!uri.contains(".") || uri.contains("favicon.ico")) {
     
            return true;
        }
        return false;
    }


    public void setBasePath(String basePath) {
     
        this.basePath = basePath;
    }
}

2.3 添加通道处理器

public class MyStaticWebInitializer extends ChannelInitializer {
     
    protected void initChannel(Channel ch) throws Exception {
     
        // Http相关的编解码器
        ch.pipeline().addLast("http-decoder", new HttpRequestDecoder());
        ch.pipeline().addLast("http-aggregator", new HttpObjectAggregator(65536));
        ch.pipeline().addLast("http-encoder", new HttpResponseEncoder());
        // 文件分块
        ch.pipeline().addLast("http-chunked", new ChunkedWriteHandler());

        // 自定义文件处理器
        ch.pipeline().addLast("http-fileHandler", new MyFileHandler());
    }
}

2.4 文件发送进度监控

  • 如果不需要监控文件发送进度可以直接在handler中注释掉监听即可
public class MyChannelProgressiveFutureListener implements ChannelProgressiveFutureListener {
     
    public void operationComplete(ChannelProgressiveFuture future)
            throws Exception {
     
        System.out.println("Transfer complete.");

    }
    public void operationProgressed(ChannelProgressiveFuture future,
                                    long progress, long total) throws Exception {
     
        if(total < 0)
            System.err.println("Transfer progress: " + progress);
        else
            System.err.println("Transfer progress: " + progress + "/" + total);
    }
}

3. 启动服务

  • 启动服务之后直接在浏览器中访问基本目录(MyFileHandler中basePath)的文件,示列url:http://localhost:8888/hello.html , 注意默认的basePath就是电脑的D盘根目录
public class Entry {
     
    public static void main(String[] args) {
     
        MyStaticWebServer webServer = new MyStaticWebServer();
        webServer.start();
    }
}

你可能感兴趣的:(Netty,HTTP文件服务,Netty实现静态文件服务器,Netty实现HTTP文件服务,静态文件服务)