netty使用http协议开发文件服务器

本文通过netty实现一个简单的文件服务器,能够像nginx一样将目录结构列举出来并且支持下载

         1、 对应目录:    点击文件目录可以进行下钻, 而且可以通过 ".." 进行回退到上一个目录。

         2、对应文件:    支持下载。

一、首先先编写服务启动类,配置好netty的启动参数及pipeline。相关代码如下:

public class HttpServer {

    //绑定端口
    private int port;
    //boss线程
    private NioEventLoopGroup boss;
    //工作线程
    private NioEventLoopGroup work;
    public HttpServer(int port) {
        
        this.port = port;
    }
    
    /**
     * 开启服务
     */
    public void start() {
        
        //boss线程一般设置为1
        boss = new NioEventLoopGroup(1);
        /**
         * 工作线程数使用默认值,cup核数*2
         */
        work = new NioEventLoopGroup();
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(boss, work)
            .option(ChannelOption.SO_BACKLOG, 100)
            .childOption(ChannelOption.SO_KEEPALIVE, true)           //保持长连接
            .channel(NioServerSocketChannel.class)
            //打印日志
            .handler(new LoggingHandler(LogLevel.DEBUG))
            .childHandler(new ChannelInitializer<SocketChannel>() {

                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    //设置 http请求编码解码器,使得系统支持http协议的请求
                    ch.pipeline().addLast("http-codec",new HttpServerCodec());
                    //在http请求的编解码器后设置HttpObjectAggregator,其配合HttpServerCodec完成封装http请求。因为一个完整
                    //的请求会被解码成多个http message。
                    ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65535));
                    //ChunkedWriteHandler它增加了对异步写入大数据流的支持,既不占用大量内存
                    ch.pipeline().addLast("http-chunked",new ChunkedWriteHandler());
                    //自己的业务处理类,文件服务器。
                    ch.pipeline().addLast("http-file",new HttpFileServerHandler());
                }
            });
        try {
            ChannelFuture c = serverBootstrap.bind(port).sync();
            c.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }

    /**
     * 停止服务
     */
    public void stop(){
        boss.shutdownGracefully();
        work.shutdownGracefully();
    }
}
 

 

二、写文件服务实现逻辑:

public class HttpFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {

    private final File root = new File("D:/");
    private final String charsetName = "utf-8";
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
        
        String uri = URLDecoder.decode(msg.uri(), "utf-8");
        if(uri.contains("favicon.ico")) {
            sendError(ctx);
            return ;
        }
        File file;
        if(uri.length()==0 || uri.equals("/")) {
            file = root;
        } else {
            file = new File(root, uri);
        }
        if(file.isDirectory()) {
            //响应目录结构
            responseDirectory(file,msg,ctx);
            return ;
        } else if(file.isFile()) {
            //文件下载
            responseFile(file,msg,ctx);
            return ;
        }
        sendError(ctx);
    }
    private void sendError(ChannelHandlerContext ctx) {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
        response.headers().set(HttpHeaderNames.CONNECTION, "keep-alive");
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=utf-8");
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, 0);
        ctx.writeAndFlush(response);
    }
    private void responseFile(File file, FullHttpRequest msg, ChannelHandlerContext ctx) {

        HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        //设置请求头方面的信息
        response.headers().set(HttpHeaderNames.CONNECTION, "keep-alive");
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_OCTET_STREAM);
        response.headers().set(HttpHeaderNames.CONTENT_DISPOSITION, "attachment; filename=" + file.getName());
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, file.length());
        ctx.write(response);

        //文件下载,下载完成后输出下载完成提示信息
        DefaultFileRegion fileRegin = new DefaultFileRegion(file, 0, file.length());
        ctx.write(fileRegin).addListener(new GenericFutureListener<Future<? super Void>>() {
            @Override
            public void operationComplete(Future<? super Void> future) throws Exception {
                System.out.println("下载完成");
            }
        });
        //文件下载结束标记
        ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
    }
    private void responseDirectory(File root, FullHttpRequest msg, ChannelHandlerContext ctx) throws IOException {
        FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
        response.headers().set(HttpHeaderNames.CONNECTION, "keep-alive");
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html; charset=utf-8");
        File[] files = root.listFiles();
        
        //构建文件目录结构,
        StringBuilder b = new StringBuilder();
        if(root != this.root) {
            
            b.append("<a href='/");
            b.append(root.getParent().substring(3));
            b.append("'>..</a></br>");
        }
        for (File file : files) {
            b.append("<a ");
            b.append("href='");
            b.append("/"+file.getCanonicalPath().substring(3));
            b.append("'>");
            b.append(file.getName());
            b.append("</a><br/>");
        }
        byte[] u = b.toString().getBytes(charsetName);
        response.headers().set(HttpHeaderNames.CONTENT_LENGTH, u.length);
        response.content().writeBytes(u);
        ctx.writeAndFlush(response);
    }

}

 

三、 运行服务

public class Start {

    public static void main(String[] args) {
        HttpServer server = new HttpServer(8899);
        server.start();
    }
}

四、通过 http://127.0.0.1:8899 进行访问 ;实现效果如下

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值