Netty之二进制文件传输

传输会话简要

客户端发起一个文本请求给服务器端, 服务器端解析里面文本, 返回文件给客户端, 客户端解析文件

服务器端

因为示例文件比较小, 所以没有做分段传输, 而是直接一次性把整个文件byte[]都发给客户端了.

如果需要传输大文件, 则需要做粘包拆包, 参考另外一篇博文 Netty之粘包分包

需要三个ChannelPipeline

1                             // 解析客户端发送的文本json
2                             pipeline.addLast(new StringDecoder());
3                             // 二进制文件加密传输
4                             pipeline.addLast(new ObjectEncoder());
5                             // 业务逻辑
6                             pipeline.addLast(new FileServerHandler());

FileServerHandler业务逻辑

            // 获取到客户端请求, 解析path, 返回二进制文件
            JSONObject jo = new JSONObject(msg.toString());
            if (StringUtils.isNotEmpty(jo.optString("path"))) {
                PDFContent pdf = new PDFContent();
                byte[] content = com.fr.general.IOUtils.inputStream2Bytes(new FileInputStream(jo.optString("path")));
                pdf.setContent(content);
                ctx.writeAndFlush(pdf);
            } else {
                System.out.println(jo.optString("res"));
            }

客户端

跟服务器端对应的三个ChannelPipeline

1                             // 传输文本给服务器端
2                             p.addLast(new StringEncoder());
3                             // 二进制文件获取解析
4                             p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this
5                                     .getClass().getClassLoader())));
6                             // 客户端业务代码
7                             p.addLast(new FileClientHandler());

FileClientHandler业务逻辑

 1         @Override
 2         public void channelActive(ChannelHandlerContext ctx) {
 3             try {
 4                 // 将要获取的pdf路径发送给服务器端
 5                 JSONObject jo = JSONObject.create().put("path", "d:\\a.pdf");
 6                 ctx.writeAndFlush(jo.toString());
 7             } catch (JSONException e) {
 8                 e.printStackTrace();
 9             }
10         }
11 
12         @Override
13         public void channelRead(ChannelHandlerContext ctx, Object msg) {
14             PDFContent content = (PDFContent) msg;
15             // 从服务器端获取的二进制文件存到本地
16             String fileName = UUID.randomUUID().toString() + ".pdf";
17             File file = new File("D:\\" + fileName);
18             try {
19                 FileOutputStream out = new FileOutputStream(file);
20                 IOUtils.copyBinaryTo(new ByteArrayInputStream(content.getContent()), out);
21                 out.close();
22             } catch (FileNotFoundException e) {
23                 e.printStackTrace();
24             } catch (IOException e) {
25                 e.printStackTrace();
26             }
27             try {
28                 JSONObject jo = JSONObject.create().put("res", "Thank You, I Have The File!");
29                 ctx.writeAndFlush(jo.toString());
30                 ctx.close();
31             } catch (JSONException e) {
32                 e.printStackTrace();
33             }
34         }

完整的代码如下

FileClient & FileClientHandler

  1 package test;
  2 
  3 import com.fr.general.IOUtils;
  4 import com.fr.json.JSONException;
  5 import com.fr.json.JSONObject;
  6 import com.fr.stable.core.UUID;
  7 import io.netty.bootstrap.Bootstrap;
  8 import io.netty.channel.ChannelFuture;
  9 import io.netty.channel.ChannelHandlerContext;
 10 import io.netty.channel.ChannelInboundHandlerAdapter;
 11 import io.netty.channel.ChannelInitializer;
 12 import io.netty.channel.ChannelPipeline;
 13 import io.netty.channel.EventLoopGroup;
 14 import io.netty.channel.nio.NioEventLoopGroup;
 15 import io.netty.channel.socket.SocketChannel;
 16 import io.netty.channel.socket.nio.NioSocketChannel;
 17 import io.netty.handler.codec.serialization.ClassResolvers;
 18 import io.netty.handler.codec.serialization.ObjectDecoder;
 19 import io.netty.handler.codec.string.StringEncoder;
 20 
 21 import java.io.ByteArrayInputStream;
 22 import java.io.File;
 23 import java.io.FileNotFoundException;
 24 import java.io.FileOutputStream;
 25 import java.io.IOException;
 26 
 27 public class FileClient {
 28 
 29     public FileClient(){
 30 
 31     }
 32 
 33     public void start() {
 34         EventLoopGroup group = new NioEventLoopGroup();
 35         try {
 36             Bootstrap bootstrap = new Bootstrap();
 37             bootstrap.group(group)
 38                     .channel(NioSocketChannel.class)
 39                     .handler(new ChannelInitializer<SocketChannel>() {
 40 
 41                         @Override
 42                         protected void initChannel(SocketChannel s) throws Exception {
 43                             ChannelPipeline p = s.pipeline();
 44                             // 传输文本给服务器端
 45                             p.addLast(new StringEncoder());
 46                             // 二进制文件获取解析
 47                             p.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(this
 48                                     .getClass().getClassLoader())));
 49                             // 客户端业务代码
 50                             p.addLast(new FileClientHandler());
 51                         }
 52                     });
 53             ChannelFuture future = bootstrap.connect("localhost", 7766).sync();
 54             future.channel().closeFuture().sync();
 55         } catch (InterruptedException e) {
 56             e.printStackTrace();
 57         } finally {
 58             group.shutdownGracefully();
 59         }
 60     }
 61 
 62     public static void main(String[] args) throws InterruptedException {
 63         new FileClient().start();
 64     }
 65 
 66     private static class FileClientHandler extends ChannelInboundHandlerAdapter {
 67 
 68 
 69         @Override
 70         public void channelActive(ChannelHandlerContext ctx) {
 71             try {
 72                 // 将要获取的pdf路径发送给服务器端
 73                 JSONObject jo = JSONObject.create().put("path", "d:\\a.pdf");
 74                 ctx.writeAndFlush(jo.toString());
 75             } catch (JSONException e) {
 76                 e.printStackTrace();
 77             }
 78         }
 79 
 80         @Override
 81         public void channelRead(ChannelHandlerContext ctx, Object msg) {
 82             PDFContent content = (PDFContent) msg;
 83             // 从服务器端获取的二进制文件存到本地
 84             String fileName = UUID.randomUUID().toString() + ".pdf";
 85             File file = new File("D:\\" + fileName);
 86             try {
 87                 FileOutputStream out = new FileOutputStream(file);
 88                 IOUtils.copyBinaryTo(new ByteArrayInputStream(content.getContent()), out);
 89                 out.close();
 90             } catch (FileNotFoundException e) {
 91                 e.printStackTrace();
 92             } catch (IOException e) {
 93                 e.printStackTrace();
 94             }
 95             try {
 96                 JSONObject jo = JSONObject.create().put("res", "Thank You, I Have The File!");
 97                 ctx.writeAndFlush(jo.toString());
 98                 ctx.close();
 99             } catch (JSONException e) {
100                 e.printStackTrace();
101             }
102         }
103 
104         @Override
105         public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
106             cause.printStackTrace();
107             ctx.close();
108         }
109     }
110 }

FileServer & FileServerHandler

 1 package test;
 2 
 3 import com.fr.json.JSONObject;
 4 import com.fr.stable.StringUtils;
 5 import io.netty.bootstrap.ServerBootstrap;
 6 import io.netty.channel.ChannelFuture;
 7 import io.netty.channel.ChannelHandlerContext;
 8 import io.netty.channel.ChannelInboundHandlerAdapter;
 9 import io.netty.channel.ChannelInitializer;
10 import io.netty.channel.ChannelOption;
11 import io.netty.channel.ChannelPipeline;
12 import io.netty.channel.EventLoopGroup;
13 import io.netty.channel.nio.NioEventLoopGroup;
14 import io.netty.channel.socket.SocketChannel;
15 import io.netty.channel.socket.nio.NioServerSocketChannel;
16 import io.netty.handler.codec.serialization.ObjectEncoder;
17 import io.netty.handler.codec.string.StringDecoder;
18 import io.netty.handler.logging.LogLevel;
19 import io.netty.handler.logging.LoggingHandler;
20 
21 import java.io.FileInputStream;
22 
23 public class FileServer {
24 
25     private FileServer() {
26         startServer();
27     }
28 
29     private void startServer() {
30         EventLoopGroup bossGroup = new NioEventLoopGroup(1);
31         EventLoopGroup workerGroup = new NioEventLoopGroup();
32         try{
33             ServerBootstrap bootstrap = new ServerBootstrap();
34             bootstrap.group(bossGroup, workerGroup)
35                     .channel(NioServerSocketChannel.class)
36                     .option(ChannelOption.SO_BACKLOG, 100)
37                     .handler(new LoggingHandler(LogLevel.INFO))
38                     .childHandler(new ChannelInitializer<SocketChannel>() {
39                         @Override
40                         protected void initChannel(SocketChannel ch) throws Exception {
41                             ChannelPipeline pipeline = ch.pipeline();
42                             // 解析客户端发送的文本json
43                             pipeline.addLast(new StringDecoder());
44                             // 二进制文件加密传输
45                             pipeline.addLast(new ObjectEncoder());
46                             // 业务逻辑
47                             pipeline.addLast(new FileServerHandler());
48                         }
49                     });
50             ChannelFuture future = bootstrap.bind("localhost", 7766).sync();
51             future.channel().closeFuture().sync();
52         } catch (InterruptedException e) {
53             e.printStackTrace();
54         } finally {
55             bossGroup.shutdownGracefully();
56             workerGroup.shutdownGracefully();
57         }
58 
59     }
60 
61     private class FileServerHandler extends ChannelInboundHandlerAdapter {
62 
63         @Override
64         public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
65             // 获取到客户端请求, 解析path, 返回二进制文件
66             JSONObject jo = new JSONObject(msg.toString());
67             if (StringUtils.isNotEmpty(jo.optString("path"))) {
68                 PDFContent pdf = new PDFContent();
69                 byte[] content = com.fr.general.IOUtils.inputStream2Bytes(new FileInputStream(jo.optString("path")));
70                 pdf.setContent(content);
71                 ctx.writeAndFlush(pdf);
72             } else {
73                 System.out.println(jo.optString("res"));
74             }
75         }
76 
77         @Override
78         public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
79             cause.printStackTrace();
80             ctx.close();
81         }
82     }
83 
84     public static void main(String[] args){
85         // 启动Server
86         new FileServer();
87     }
88 
89 
90 }

PDFContent

 1 package test;
 2 
 3 import java.io.Serializable;
 4 
 5 /**
 6  * 文件的封装
 7  */
 8 public class PDFContent implements Serializable{
 9 
10     private byte[] content;
11 
12     public byte[] getContent() {
13         return content;
14     }
15 
16     public void setContent(byte[] content) {
17         this.content = content;
18     }
19 }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java架构师学习

Zookeeper-watcher机制源码分析(二)

其大致流程如下   ① 通过传入的path(节点路径)从watchTable获取相应的watcher集合,进入②

15910
来自专栏c#开发者

MSMQ突破4M限制的方法

    在默认情况下msmq 3.0(windows xp ,windows 2003)最大单个消息(Message size)大小4M;(包括正文和全部指定属...

36440
来自专栏DOTNET

asp.net web api 下载之断点续传

一、基本思想 利用 HTTP 请求的Range标头值,来向服务端传递请求数据的开始位置和结束位置。服务端获得这两个参数后,将指定范围内的数据传递给客户端。当客户...

480120
来自专栏码匠的流水账

频繁产生对象造成gc时间过长案例分析

gc时间过长,平均gc pause的时间要将近4秒,有13%的gc超过10秒,太可怕了,部分gc日志如下:

22610
来自专栏石奈子的Java之路

原 SpringBoot 2.0 系列00

14640
来自专栏菩提树下的杨过

java学习:weblogic下JNDI及JDBC连接测试(weblogic环境)

JNDI的专业解释,大家自行去网络搜索吧,这里就不啰嗦了。 单纯从使用角度看,可以简称把它看成一个key-value的“哈希资源”容器。给定一个string类型...

30190
来自专栏walterlv - 吕毅的博客

.NET 中 GetProcess 相关方法的性能

2018-08-19 07:04

8930
来自专栏个人分享

初版storm项目全流程自动化测试代码实现

  由于项目需要,写了版针对业务的自动化测试代码,主要应用场景在于由于业务日趋复杂,一些公共代码的改动,担心会影响已有业务。还没进行重写,但知识点还是不少的与大...

9310
来自专栏chenssy

【追光者系列】HikariCP源码分析之evict、时钟回拨、连接创建生命周期

evict定义在com.zaxxer.hikari.pool.PoolEntry中,evict的汉语意思是驱逐、逐出,用来标记连接池中的连接不可用。

47440
来自专栏Java成神之路

Java企业微信开发_05_消息推送之被动回复消息

微信加解密包 下载地址:http://qydev.weixin.qq.com/java.zip      ,此包中封装好了AES加解密方法,直接调用方法即可。

36420

扫码关注云+社区

领取腾讯云代金券