一、简介
使用Netty进行文件传输主要涉及到FileChannel文件通道,它用来连接文件,可以通过这个通道读写文件。在使用FileChannel之前必须先打开它,FileChannel无法直接打开,可以通过InputStream、OutputStream或RandomAccessFile来获取FileChannel实例,比如:
1
2
3
4 1RandomAccessFile file=new RandomAccessFile("D:\test.txt","rw");
2FileChannel channel=file.getChannel();
3
4
如果需要从FileChannel中读取数据,要申请一个ByteBuffer,将数据从FileChannel读取到缓冲区中。read()方法返回的int值表示有多少个字节被读取到了缓冲区中,如果返回-1,表示读取到了文件末尾。
如果需要通过FileChannel向文件中写入数据,需要将数据复制或直接写入到ByteBuffer中,然后调用FileChannel.write()方法进行写操作,比如:
1
2
3
4
5
6
7 1String content="test filechannel";
2ByteBuffer writeBuffer=ByteBuffer.allocate(1024);
3writeBuffer.put(content.getBytes());
4writeBuffer.flip();
5channel.write(buf);
6
7
使用完FileChannel之后需要通过close()方法关闭文件句柄,否则可能出现句柄泄漏。
可以通过FileChannel的position(long pos)方法设置文件的位置指针,通过这种方式可以实现文件的随机读写。
二、Netty文件传输服务端
实现步骤如下:
1)Netty文件服务器启动,绑定8888作为内部监听端口;
2)在命令提示符窗口,通过telnet和文件服务器建立TCP连接;
3)在控制台输入需要下载的文件的绝对路径;
4)文件服务器接收到请求后进行合法性判断,如果文件存在,就将文件发送给控制台;
5)控制台打印文件名和文件内容。
下面是服务端实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 1import io.netty.bootstrap.ServerBootstrap;
2import io.netty.channel.ChannelFuture;
3import io.netty.channel.ChannelInitializer;
4import io.netty.channel.ChannelOption;
5import io.netty.channel.EventLoopGroup;
6import io.netty.channel.nio.NioEventLoopGroup;
7import io.netty.channel.socket.SocketChannel;
8import io.netty.channel.socket.nio.NioServerSocketChannel;
9import io.netty.handler.codec.LineBasedFrameDecoder;
10import io.netty.handler.codec.string.StringDecoder;
11import io.netty.handler.codec.string.StringEncoder;
12import io.netty.handler.logging.LogLevel;
13import io.netty.handler.logging.LoggingHandler;
14import io.netty.util.CharsetUtil;
15
16public class FileServer
17{
18 public void run (int port)throws Exception{
19 EventLoopGroup bossGroup=new NioEventLoopGroup();
20 EventLoopGroup workerGroup=new NioEventLoopGroup();
21 try
22 {
23 ServerBootstrap b=new ServerBootstrap();
24 b.group(bossGroup,workerGroup)
25 .channel(NioServerSocketChannel.class)
26 .option(ChannelOption.SO_BACKLOG, 100)
27 .handler(new LoggingHandler(LogLevel.INFO))
28 .childHandler(new ChannelInitializer<SocketChannel>()
29 {
30
31 @Override
32 protected void initChannel(SocketChannel ch)
33 throws Exception
34 {
35 ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8),
36 //按照回车换行符对数据包进行解码
37 new LineBasedFrameDecoder(1024),
38 new StringDecoder(CharsetUtil.UTF_8),
39 new FileServerHandler());
40 }
41 });
42 ChannelFuture f=b.bind(port).sync();
43 System.out.println("Start file server at port : "+port);
44 f.channel().closeFuture().sync();
45 }
46 catch (Exception e)
47 {
48 e.printStackTrace();
49 }
50 finally{
51 bossGroup.shutdownGracefully();
52 workerGroup.shutdownGracefully();
53 }
54 }
55
56 public static void main(String[] args)throws Exception
57 {
58 int port =8888;
59 try
60 {
61 if (args!=null&&args.length>0)
62 {
63 port=Integer.valueOf(args[0]);
64 }
65 }
66 catch (Exception e)
67 {
68 e.printStackTrace();
69 }
70 new FileServer().run(port);
71 }
72}
73
74
下面是网络
IO
事件的处理:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 1import java.io.File;
2import java.io.RandomAccessFile;
3
4import io.netty.channel.ChannelHandlerContext;
5import io.netty.channel.DefaultFileRegion;
6import io.netty.channel.FileRegion;
7import io.netty.channel.SimpleChannelInboundHandler;
8
9public class FileServerHandler extends SimpleChannelInboundHandler<String>
10{
11 //操作系统识别的换行符
12 private static final String CR=System.getProperty("line.separator");
13
14 @Override
15 protected void messageReceived(ChannelHandlerContext ctx, String msg)
16 throws Exception
17 {
18 File file=new File(msg);
19 if (file.exists())
20 {
21 if (!file.isFile())
22 {
23 //写入换行符表示文件结束
24 ctx.writeAndFlush("Not a file: "+file+CR);
25 return;
26 }
27 //换行符表示文件结尾
28 ctx.write(file+" "+file.length()+CR);
29 RandomAccessFile randomAccessFile=new RandomAccessFile(msg, "r");
30 FileRegion region=new DefaultFileRegion(
31 randomAccessFile.getChannel(), 0, randomAccessFile.length());
32 ctx.write(region);
33 //写入换行符表示文件结束
34 ctx.writeAndFlush(CR);
35 randomAccessFile.close();
36 }else {
37 ctx.writeAndFlush("File not found: "+file+CR);
38 }
39 }
40
41 @Override
42 public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause){
43 cause.printStackTrace();
44 ctx.close();
45 }
46}
47
48
三、测试
启动文件服务器服务端后,打开命令提示符窗口,输入
telnet localhost 8888
和文件服务器建立
TCP
连接,然后输入一个文件路径进行验证:
参考书籍《Netty权威指南》