概述
在 Netty学习(2)中,我们先浅浅认识了 NIO 的3大核心组件,现在就让我们针对其深入学习,通过一些简单的文件操作来深入理解其中的 Buffer 和 Channel 的概念。
文件写入
将内存中的数据写入到文件中,如果文件不存在,那么就新建文件。
1// 数据 -> 文件 2 private static void dataToFile(String data, String filePath) { 3 // 构建输出流 -> 从输出流中获取 channel 4 try (FileOutputStream fileOutputStream = new FileOutputStream(filePath); 5 FileChannel fileChannel = fileOutputStream.getChannel()) { 6 7 // 设置缓冲区 8 ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH); 9 10 // 将需要读写的数据放到缓冲区 11 int i = 0; 12 int length = data.getBytes().length; 13 // 一次就可以读完 14 if (BYTE_BUFFER_LENGTH > data.getBytes().length) { 15 byteBuffer.put(data.getBytes(), i, data.getBytes().length); 16 byteBuffer.flip(); 17 fileChannel.write(byteBuffer); 18 } else { 19 // 一次读不完 需要循环读取 20 for (int temp = 0; temp < data.getBytes().length; temp += BYTE_BUFFER_LENGTH) { 21 byteBuffer.clear(); 22 byteBuffer.put(data.getBytes(), temp, BYTE_BUFFER_LENGTH); 23 // 翻转缓冲区,可以对外读 24 // 这里的 flip() 是重点,其可以将Buffer的属性重置,可以对外写 25 byteBuffer.flip(); 26 // 将缓冲区内的数据写到 channel中 27 fileChannel.write(byteBuffer); 28 } 29 } 30 } catch (Exception e) { 31 e.printStackTrace(); 32 } 33 }
这样,我们就写完了一个文件写入的函数,在需要时传入指定的字符串即可。
文件读取
从文件中读取数据,并将其输出到控制台中。
1 // 文件 -> 内存 2 private static void dataFromFile(String filePath) { 3 File file = new File(filePath); 4 // 从输入流中获取 channel 5 try (FileInputStream fileInputStream = new FileInputStream(file); 6 FileChannel channel = fileInputStream.getChannel()) { 7 8 // 分配缓冲区 9 ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH); 10 StringBuilder result = new StringBuilder(); 11 while (true) { 12 byteBuffer.clear(); 13 // 将 channel数据写到buffer中 14 int read = channel.read(byteBuffer); 15 // 因为byteBuffer大小原因,因此需要用一个中间字符串接受一下 16 result.append(new String(byteBuffer.array())); 17 if (read == -1) { 18 break; 19 } 20 } 21 22 logger.info("从文本读取结果:{}", result); 23 } catch (Exception e) { 24 logger.error("文件读取错误,错误原因 :{}", e); 25 } 26 }
文件拷贝
用 NIO 来完成文件拷贝,有两种实现方式,一种是用 Buffer 完成两个文件之间数据的转移,另一种是直接使用 Channel 来完成文件复制。
Buffer 完成
通过 Buffer 来完成文件复制,步骤如下:
-
获取源文件(source)和目标文件(target)的 channel;
-
设置缓冲区;
-
在循环中,通过缓冲区,将 source 的数据写入到 target 的 channel 中,完成写入,即复制成功。
// 将两个channel通过byteBuffer进行转移 private static void copyFileUseBuffer(String sourceFilePath, String targetFilePath) { File source = new File(sourceFilePath); File target = new File(targetFilePath); // 获取文件输入输出流 // 从输入输出流中获取输入输出 channel
1 try (FileInputStream fileInputStream = new FileInputStream(source); 2 FileOutputStream fileOutputStream = new FileOutputStream(target); 3 FileChannel fileInputStreamChannel = fileInputStream.getChannel(); 4 FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) { 5 6 // 分配缓冲区 7 ByteBuffer byteBuffer = ByteBuffer.allocate(BYTE_BUFFER_LENGTH); 8 // 将输入流中的数据写到缓冲区 9 // 这里需要循环读取,如果是大文件,不能直接建立一个很大的内存空间,直接全部放进去,并且还可能放不进去 10 while (true) { 11 byteBuffer.clear(); 12 13 int read = fileInputStreamChannel.read(byteBuffer); 14 if (read == -1) { 15 break; 16 } 17 // 翻转缓冲区 18 byteBuffer.flip(); 19 20 // 将翻转后可以对外写的缓存区的内容写到输出流,从而形成文件 21 fileOutputStreamChannel.write(byteBuffer); 22 } 23 } catch (Exception e) { 24 logger.error("文件复制错误,错误原因 :{0}", e); 25 }
Channel 完成
但其实,Java 官方也考虑到这个需求,其内置了一个通道复制的函数,可以直接完成复制。
1// 直接用channel的复制完成文件复制 2 private static void copyFileUseChannelTransfer(String sourceFilePath, String targetFilePath) { 3 File source = new File(sourceFilePath); 4 File target = new File(targetFilePath); 5 // 获取文件输入输出流 6 // 从输入输出流中获取输入输出 channel 7 try (FileInputStream fileInputStream = new FileInputStream(source); 8 FileOutputStream fileOutputStream = new FileOutputStream(target); 9 FileChannel fileInputStreamChannel = fileInputStream.getChannel(); 10 FileChannel fileOutputStreamChannel = fileOutputStream.getChannel()) { 11 12 // 直接将输入channel复制到输出channel 13 fileOutputStreamChannel.transferFrom(fileInputStreamChannel, fileInputStreamChannel.position(), fileInputStreamChannel.size()); 14 15 } catch (Exception e) { 16 logger.error("文件复制错误,错误原因 :{0}", e); 17 } 18 }
总结
本文,我们通过文件的读取,写入,复制,从而理解了 Buffer 和 Channel 的作用和使用方式,在后续的网络编程中,我们还要用到这些操作方式,以便逐步深入到 Netty 的学习范围。
本文中代码已上传到 GitHub 上,地址为 https://github.com/wb1069003157/nettyPre-research ,欢迎大家来讨论,探讨。

文章在公众号「iceWang」第一手更新,有兴趣的朋友可以关注公众号,第一时间看到笔者分享的各项知识点,谢谢!笔芯!