使用netty实现文件上传服务器

使用netty实现文件上传服务器

代码实现根据官网提供的example https://github.com/netty/nett...
以及netty官网的api文档 https://netty.io/4.1/api/inde...
项目地址 https://github.com/1433365571...

1 编写 server 启动类

package server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.SelfSignedCertificate;

public final class HttpUploadServer {

    static final boolean SSL = System.getProperty("ssl") != null;

    static final int PORT = Integer.parseInt(System.getProperty("port", SSL ? "8443" : "8080"));

    public static void main(String[] args) throws Exception {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {

            ServerBootstrap b = new ServerBootstrap();

            b.group(bossGroup, workerGroup);

            b.channel(NioServerSocketChannel.class);

            b.handler(new LoggingHandler(LogLevel.INFO));

            b.childHandler(new HttpUploadServerInitializer(sslCtx));

            Channel ch = b.bind(PORT).sync().channel();

            System.err.println("Open your web browser and navigate to " +
                    (SSL ? "https" : "http") + "://127.0.0.1:" + PORT + '/');

            ch.closeFuture().sync();

        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2 绑定handler

/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.ssl.SslContext;

public class HttpUploadServerInitializer extends ChannelInitializer {

    private final SslContext sslCtx;

    public HttpUploadServerInitializer(SslContext sslCtx) {
        this.sslCtx = sslCtx;
    }

    @Override
    public void initChannel(SocketChannel ch) {

        ChannelPipeline pipeline = ch.pipeline();

        if (sslCtx != null) {

            pipeline.addLast(sslCtx.newHandler(ch.alloc()));

        }

        pipeline.addLast(new HttpRequestDecoder());

        pipeline.addLast(new HttpResponseEncoder());

        // Remove the following line if you don't want automatic content compression.
        pipeline.addLast(new HttpContentCompressor());

//        pipeline.addLast("http-aggregator",
//                new HttpObjectAggregator(65536));// 目的是将多个消息转换为单一的request或者response对象

        pipeline.addLast(new HttpUploadServerHandler());
    }


}

3 上传处理的handler


package server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import io.netty.util.CharsetUtil;

import java.io.File;
import java.io.IOException;
import java.net.URI;

public class HttpUploadServerHandler extends SimpleChannelInboundHandler {

    private HttpRequest request;

    private static final String uploadUrl = "/up";

    private static final String fromFileUrl = "/post_multipart";

    private static final HttpDataFactory factory =
            new DefaultHttpDataFactory(DefaultHttpDataFactory.MINSIZE); // Disk if size exceed

    private HttpPostRequestDecoder decoder;

    static {
        DiskFileUpload.deleteOnExitTemporaryFile = true; // should delete file
        // on exit (in normal
        // exit)
        DiskFileUpload.baseDirectory = null; // system temp directory
        DiskAttribute.deleteOnExitTemporaryFile = true; // should delete file on
        // exit (in normal exit)
        DiskAttribute.baseDirectory = null; // system temp directory
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        if (decoder != null) {
            decoder.cleanFiles();
        }
    }

    protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) throws Exception {

        if (msg instanceof HttpRequest) {

            this.request = (HttpRequest) msg;

            URI uri = new URI(request.uri());

            System.out.println(uri);

            urlRoute(ctx, uri.getPath());

        }

        if (decoder != null) {

            if (msg instanceof HttpContent) {

                // 接收一个新的请求体
                decoder.offer((HttpContent) msg);
                // 将内存中的数据序列化本地
                readHttpDataChunkByChunk();

            }

            if (msg instanceof LastHttpContent) {

                System.out.println("LastHttpContent");

                reset();

                writeResponse(ctx, "

上传成功

"); } } } // url路由 private void urlRoute(ChannelHandlerContext ctx, String uri) { StringBuilder urlResponse = new StringBuilder(); // 访问文件上传页面 if (uri.startsWith(uploadUrl)) { urlResponse.append(getUploadResponse()); } else if (uri.startsWith(fromFileUrl)) { decoder = new HttpPostRequestDecoder(factory, request); return; } else { urlResponse.append(getHomeResponse()); } writeResponse(ctx, urlResponse.toString()); } private void writeResponse(ChannelHandlerContext ctx, String context) { ByteBuf buf = Unpooled.copiedBuffer(context, CharsetUtil.UTF_8); FullHttpResponse response = new DefaultFullHttpResponse( HttpVersion.HTTP_1_1, HttpResponseStatus.OK, buf); response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/html;charset=utf-8"); //设置短连接 addListener 写完马上关闭连接 ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); } private String getHomeResponse() { return "

welcome home

"; } private String getUploadResponse() { return "\n" + "\n" + "\n" + " \n" + " Title\n" + "\n" + "\n" + "\n" + "
\n" + "\n" + "\n" + " \n" + "\n" + " \n" + "\n" + "
\n" + "\n" + "\n" + ""; } private void readHttpDataChunkByChunk() throws IOException { while (decoder.hasNext()) { InterfaceHttpData data = decoder.next(); if (data != null) { if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.FileUpload) { FileUpload fileUpload = (FileUpload) data; if (fileUpload.isCompleted()) { fileUpload.isInMemory();// tells if the file is in Memory // or on File fileUpload.renameTo(new File(PathUtil.getFileBasePath() + fileUpload.getFilename())); // enable to move into another // File dest decoder.removeHttpDataFromClean(fileUpload); //remove } } } } } private void reset() { request = null; // destroy the decoder to release all resources decoder.destroy(); decoder = null; } }

4 环境文件

package server;

import java.io.File;

public class PathUtil {
    private static final ClassLoader classLoader = PathUtil.class.getClassLoader();

    public static String getFileBasePath() {
        String os = System.getProperty("os.name");
        String basePath;
        if (os.toLowerCase().startsWith("win")) {
            basePath = "D:/warehouse/";
        } else {
            basePath = "/root/upload_source";
        }
        basePath = basePath.replace("/", File.separator);
        return basePath;
    }

    public static String getSourcePath(String name) {
        return classLoader.getResource(name).getPath();
    }
}

5 pom文件



    4.0.0

    nettyHttpUploadServer
    http.upload
    1.0-SNAPSHOT


    
        UTF-8
    

    

        
        
            io.netty
            netty-all
            4.1.10.Final
        

    

    
    
    
    
    

    
    
    
    
    
    
    
    

    
    
    
    

        
            
                maven-assembly-plugin
                3.0.0
                
                    
                        
                            server.HttpUploadServer
                        
                    
                    
                        jar-with-dependencies
                    
                
                
                    
                        make-assembly
                        package
                        
                            single
                        
                    
                
            
            
                org.apache.maven.plugins
                maven-jar-plugin
                
                    
                        
                            server.HttpUploadServer
                        
                    
                
            
            
                com.jolira
                onejar-maven-plugin
                1.4.4
                
                    
                        
                            one-jar
                        
                    
                
            
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    1.8
                    1.8
                
            
        

    



6 代码说明

  • ChannelHandlerContext 控制数据处理管道 ChannelPipeline 执行流程
  • HttpObject 消息处理的接口
  • HttpRequest 封装http请求头请求协议等
  • DefaultFullHttpResponse 构造http响应
  • httpcontext netty会将请求体分块处理此文章解释较详细,直接处理完整的请求体可以请将HttpObjectAggregator放在ChannelPipelineHttpObjectDecoder之后

使用netty实现文件上传服务器_第1张图片

  • LastHttpContent 标识Http请求结束
  • HttpDataFactory 上传文件的处理方式
  • HttpPostRequestDecoder post请求体的解析类

7 访问 http://127.0.0.1:8080/up 上传文件

你可能感兴趣的:(netty,文件上传)