Netty实战一 Netty实现文件的上传和下载

简介: Netty实战一 Netty实现文件的上传和下载

一、Netty应用场景


讲了一些Netty的组件,来聊一聊大家最关心的事情吧,他能够做什么?毕竟,我们学习就是拿来用的嘛。我可以简单的概括一下,凡是牵扯到网络相关的,都可以使用Neety去实现!


  1. 构建高性能、低时延的各种 Java 中间件,例如 MQ、分布式服务框架、ESB 消息总线等,Netty 主要作为基础通信框架提供高性能、低时延的通信服务;公有或者私有协议栈的基础通信框架,例如可以基于 Netty 构建异步、高性能的 WebSocket 协议栈;
  2. 各领域应用,例如大数据、游戏等,Netty 作为高性能的通信框架用于内部各模块的数据分发、传输和汇总等,实现模块之间高性能通信。
  3. 接下来的几篇,会围绕Netty实现相关功能进行展开。


二、Netty实现文件的上传和下载

 

1、MultipartRequest

import io.netty.handler.codec.http.multipart.FileUpload;
import org.json.simple.JSONObject;
import java.util.Map;
/**
 * <p>请求对象</p>
 *
 * @author DarkKing 
 */
public class MultipartRequest {
    private Map<String, FileUpload> fileUploads;
    private JSONObject params;
    public Map<String, FileUpload> getFileUploads() {
        return fileUploads;
    }
    public void setFileUploads(Map<String, FileUpload> fileUploads) {
        this.fileUploads = fileUploads;
    }
    public JSONObject getParams() {
        return params;
    }
    public void setParams(JSONObject params) {
        this.params = params;
    }
}


定义了一个http封装的对象。保存对应的传参数。

2、FileServer

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress;
/**
 * 作者:DarkKIng
 * 创建日期:2019/12/17
 * 类说明:文件下载服务端
 */
public class FileServer {
    private final int port;
    public FileServer(int port) {
        this.port = port;
    }
    public static void main(String[] args) throws InterruptedException {
        int port = 9999;
        FileServer fileServer = new FileServer(port);
        System.out.println("服务器即将启动");
        fileServer.start();
        System.out.println("服务器关闭");
    }
    public void start() throws InterruptedException {
        final FileServerHandle serverHandler = new FileServerHandle();
        /*线程组*/
        EventLoopGroup group = new NioEventLoopGroup();
        Pipeline pipeline = new Pipeline();
        try {
            /*服务端启动必须*/
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)/*将线程组传入*/
                    .channel(NioServerSocketChannel.class)/*指定使用NIO进行网络传输*/
                    .localAddress(new InetSocketAddress(port))/*指定服务器监听端口*/
                    /*服务端每接收到一个连接请求,就会新启一个socket通信,也就是channel,
                    所以下面这段代码的作用就是为这个子channel增加handle*/
                    .childHandler(pipeline);
            ChannelFuture f = b.bind().sync();/*异步绑定到服务器,sync()会阻塞直到完成*/
            System.out.println("Netty server  start,port is " + port);
            f.channel().closeFuture().sync();/*阻塞直到服务器的channel关闭*/
        } finally {
            group.shutdownGracefully().sync();/*优雅关闭线程组*/
        }
    }
}


 

使用netty实现服文件服务器端。

3、Pipeline

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.EventExecutorGroup;
/**
 * 作者:DarkKIng
 * 创建日期:2019/12/17
 * 作用:职责链
 */
public class Pipeline extends ChannelInitializer<SocketChannel> {
    private EventExecutorGroup businessEventExecutorGroup = new DefaultEventExecutorGroup(10);
    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        /**
         * http服务器端对response编码
         */
        pipeline.addLast("encoder", new HttpResponseEncoder());
        /**
         * http服务器端对request解码3.
         */
        pipeline.addLast("decoder", new HttpRequestDecoder());
        /**
         * 合并请求
         */
        pipeline.addLast("aggregator", new HttpObjectAggregator(655300000));
        /**
         * 正常业务逻辑处理
         */
        pipeline.addLast(businessEventExecutorGroup, new FileServerHandle());
    }
}

编写职责链,请求会从入栈以次从上到下经过编解码,请求和秉承HTTPObject,最后执行业务类FileServerHandle

4、FileServerHandle

import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.multipart.*;
import io.netty.util.CharsetUtil;
import org.json.simple.JSONObject;
import java.io.*;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
 * 作者:DarkKIng
 * 创建日期:2019/12/17
 * 类说明:文件下载handler
 */
@Sharable
public class FileServerHandle extends SimpleChannelInboundHandler<FullHttpRequest> {
    /*客户端读到数据以后,就会执行*/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request)
            throws Exception {
        //打印请求url
        System.out.println(request.uri());
        //下载任务处理
        if (request.uri().equals("/downFile")) {
            responseExportFile(ctx, "D://model.txt", "model.txt");
        }
        //上传接口处理
        if (request.uri().equals("/upLoadFile")) {
            MultipartRequest MultipartBody = getMultipartBody(request);
            Map<String, FileUpload> fileUploads = MultipartBody.getFileUploads();
            //输出文件信息
            for (String key : fileUploads.keySet()) {
                //获取文件对象
                FileUpload file = fileUploads.get(key);
                System.out.println("fileName is" + file.getFile().getPath());
                //获取文件流
                InputStream in = new FileInputStream(file.getFile());
                BufferedReader bf = new BufferedReader(new InputStreamReader(in));
                String content = bf.lines().collect(Collectors.joining("\n"));
                //打印文件
                System.out.println("content is \n" + content);
            }
            //输出参数信息
            JSONObject params = MultipartBody.getParams();
            //输出文件信息
            System.out.println(JSONObject.toJSONString(params));
        }
    }
    /*连接建立以后*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer(
                "Hello Netty", CharsetUtil.UTF_8));
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
    /**
     * <p>
     * 返回下载内容
     * </p>
     *
     * @param ctx
     * @author DarkKing 2019-12-17
     */
    public static void responseExportFile(ChannelHandlerContext ctx, String path, String name) {
        File file = new File(path);
        try {
            //随机读取文件
            final RandomAccessFile raf = new RandomAccessFile(file, "r");
            long fileLength = raf.length();
            //定义response对象
            HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
            //设置请求头部
            response.headers().set(HttpHeaderNames.CONTENT_LENGTH, fileLength);
            response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/octet-stream; charset=UTF-8");
            response.headers().add(HttpHeaderNames.CONTENT_DISPOSITION,
                    "attachment; filename=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\";");
            ctx.write(response);
            //设置事件通知对象
            ChannelFuture sendFileFuture = ctx
                    .write(new DefaultFileRegion(raf.getChannel(), 0, fileLength), ctx.newProgressivePromise());
            sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                //文件传输完成执行监听器
                @Override
                public void operationComplete(ChannelProgressiveFuture future)
                        throws Exception {
                    System.out.println("file {} transfer complete.");
                }
                //文件传输进度监听器
                @Override
                public void operationProgressed(ChannelProgressiveFuture future,
                                                long progress, long total) throws Exception {
                    if (total < 0) {
                        System.out.println("file {} transfer progress: {}");
                    } else {
                        System.out.println("file {} transfer progress: {}/{}");
                    }
                }
            });
            //刷新缓冲区数据,文件结束标志符
            ctx.writeAndFlush(LastHttpContent.EMPTY_LAST_CONTENT);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 功能描述
     * <p>解析文件上传</p>
     *
     * @author DarkKing 2019/10/9 15:24
     * @params [ctx, httpDecode]
     */
    private static MultipartRequest getMultipartBody(FullHttpRequest request) {
        try {
            //创建HTTP对象工厂
            HttpDataFactory factory = new DefaultHttpDataFactory(true);
            //使用HTTP POST解码器
            HttpPostRequestDecoder httpDecoder = new HttpPostRequestDecoder(factory, request);
            httpDecoder.setDiscardThreshold(0);
            if (httpDecoder != null) {
                //获取HTTP请求对象
                final HttpContent chunk = (HttpContent) request;
                //加载对象到加吗器。
                httpDecoder.offer(chunk);
                if (chunk instanceof LastHttpContent) {
                    //自定义对象bean
                    MultipartRequest multipartRequest = new MultipartRequest();
                    //存放文件对象
                    Map<String, FileUpload> fileUploads = new HashMap<>();
                    //存放参数对象
                    JSONObject body = new JSONObject();
                    //通过迭代器获取HTTP的内容
                    java.util.List<InterfaceHttpData> InterfaceHttpDataList = httpDecoder.getBodyHttpDatas();
                    for (InterfaceHttpData data : InterfaceHttpDataList) {
                        //如果数据类型为文件类型,则保存到fileUploads对象中
                        if (data != null && InterfaceHttpData.HttpDataType.FileUpload.equals(data.getHttpDataType())) {
                            FileUpload fileUpload = (FileUpload) data;
                            fileUploads.put(data.getName(), fileUpload);
                        }
                        //如果数据类型为参数类型,则保存到body对象中
                        if (data.getHttpDataType() == InterfaceHttpData.HttpDataType.Attribute) {
                            Attribute attribute = (Attribute) data;
                            body.put(attribute.getName(), attribute.getValue());
                        }
                    }
                    //存放文件信息
                    multipartRequest.setFileUploads(fileUploads);
                    //存放参数信息
                    multipartRequest.setParams(body);
                    return multipartRequest;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}


业务执行类,实现了文件上传和下载的接口。当请求为downFile则下载文件,请求为upLoadFile则为上传文件


三、程序演示


1、下载演示


启动服务器端


2019122520253918.png


浏览器执行下载文件,正确的下载到文件test.txt


20191225202611110.png


2、上传演示


使用apipost或者postman执行文件上传操作

20191225202603926.png


文件上传成功并成功读取文件内容



20191225202549668.png



本博文主要演示了如何不使用spring或者tomcat当做服务器,使用netty实现自己的文件上传和下载服务。并根据请求来实现对应的api接口操作。当然,如果想使用netty像spring那样简单并规范化的封装自己的api,那么就要考自己去封装实现了。有兴趣的朋友可以自己尝试下

目录
相关文章
|
2月前
Netty实战: HTTP文件列表服务器
Netty实战: HTTP文件列表服务器
35 0
|
2月前
|
Java Unix Linux
【Netty技术专题】「原理分析系列」Netty强大特性之Native transports扩展开发实战
当涉及到网络通信和高性能的Java应用程序时,Netty是一个强大的框架。它提供了许多功能和组件,其中之一是JNI传输。JNI传输是Netty的一个特性,它为特定平台提供了高效的网络传输。 在本文中,我们将深入探讨Netty提供的特定平台的JNI传输功能,分析其优势和适用场景。我们将介绍每个特定平台的JNI传输,并讨论其性能、可靠性和可扩展性。通过了解这些特定平台的JNI传输,您将能够更好地选择和配置适合您应用程序需求的网络传输方式,以实现最佳的性能和可靠性。
93 7
【Netty技术专题】「原理分析系列」Netty强大特性之Native transports扩展开发实战
|
10月前
|
Java
由浅入深Netty组件实战3
由浅入深Netty组件实战3
44 0
|
10月前
|
前端开发 算法 Java
由浅入深Netty组件实战2
由浅入深Netty组件实战2
92 0
|
10月前
|
前端开发 安全 Java
由浅入深Netty组件实战1
由浅入深Netty组件实战1
66 0
|
2月前
|
网络协议 Java 测试技术
阿里内部Netty实战小册,值得拥有
Netty 是一个高性能的 Java 网络通信框架,简化了网络编程并涵盖了最新的Web技术。它提供了一种抽象,降低了底层复杂性,使得更多开发者能接触网络编程。Netty 因其易用性、高效性和广泛的应用场景受到推崇,适合互联网行业从业者学习,有助于理解和开发基于Netty的系统。免费的《Netty实战小册》详细介绍了Netty的各个方面,包括概念、架构、编解码器、网络协议和实际案例,帮助读者深入理解和应用Netty。如需完整版小册,可点击链接获取。
阿里内部Netty实战小册,值得拥有
|
2月前
|
NoSQL Redis
Netty实战:模拟Redis的客户端
Netty实战:模拟Redis的客户端
45 0
|
2月前
|
缓存 NoSQL Java
聚焦实战技能,剖析底层原理:Netty+Redis+ZooKeeper+高并发实战
移动时代、5G时代、物联网时代的大幕已经开启,它们对于高性能、高并发的开发知识和技术的要求,抬升了Java工程师的学习台阶和面试门槛。
|
2月前
|
分布式计算 前端开发 网络协议
13W字!腾讯高工手写“Netty速成手册”,3天能走向实战
在java界,netty无疑是开发网络应用的拿手菜。你不需要太多关注复杂的nio模型和底层网络的细节,使用其丰富的接口,可以很容易的实现复杂的通讯功能。
|
2月前
|
监控 网络协议 调度
Netty Review - 深入探讨Netty的心跳检测机制:原理、实战、IdleStateHandler源码分析
Netty Review - 深入探讨Netty的心跳检测机制:原理、实战、IdleStateHandler源码分析
176 0