一、Channel(通道)介绍
通常来说NIO中的所有IO都是从 Channel(通道) 开始的。
-
从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。
-
从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。
数据读取和写入操作图示:
Java NIO Channel通道和流非常相似,主要有以下几点区别:
-
通道可以读也可以写,流一般来说是单向的(只能读或者写,所以之前我们用流进行IO操作的时候需要分别创建一个输入流和一个输出流)。
-
通道可以异步读写。
-
通道总是基于缓冲区Buffer来读写。
Java NIO中最重要的几个Channel的实现:
-
FileChannel: 用于文件的数据读写
-
DatagramChannel: 用于UDP的数据读写
-
SocketChannel: 用于TCP的数据读写,一般是客户端实现
-
ServerSocketChannel: 允许我们监听TCP链接请求,每个请求会创建会一个SocketChannel,一般是服务器实现
类层次结构:
下面的UML图使用Idea生成的。
二、FileChannel的使用
2.1、基本用法
示例一:使用FileChannel读取数据到Buffer(缓冲区)以及利用Buffer(缓冲区)写入数据到FileChannel:
1 /** 2 * 1、通道(Channel): 3 * 用于源节点与目标节点的连接。在Java NIO中负责缓冲区数据的传输。 4 * Channel本身不存储数据,因此需要缓冲区进行传输 5 * 6 * 2、通道的主要实现类 7 * java.nio.channels.Channel 接口 8 * |-- FIleChannel 9 * |-- SocketChannel 10 * |-- ServerSocketChannel 11 * |-- DatagramChannel 12 * 13 * 3、获取通道 14 * 1)Java 针对支持通道的类提供类 getChannel() 方法 15 * 本地IO: 16 * FileInputStream/FileOutputStream 17 * RandomAccessFile 18 * 19 * 网络IO: 20 * Socket 21 * ServerSocket 22 * DatagramSocket 23 * 24 * 2)在JDK 1.7 中的 NIO.2 针对各个通道提供类静态方法 open() 25 * 3)在JDK 1.7 中的 NIO.2 的Files 工具类的 newByteChannel() 26 * 27 * 4、通道之间的数据传输 28 * transferFrom() 29 * transferTo() 30 * 31 * 5、分散(Scatter)与聚集(Gather) 32 * 分散读取(Scattering Reads):将通道中的数据分散到多个缓冲区中 33 * 聚集写入(Gathering Writes):将多个缓冲区中的数据聚集到通道中 34 * 35 * 6、字符集:Charset 36 * 编码:字符串 -> 字节数组 37 * 解码:字节数组 -> 字符串 38 */ 39 public class ChannelTest { 40 41 42 // 1】利用通道完成文件的复制 43 @Test 44 public void test1(){ 45 46 FileInputStream fis = null; 47 FileOutputStream fos = null; 48 49 FileChannel inChannel = null; 50 FileChannel outChannel = null; 51 try { 52 fis = new FileInputStream("tomcat.png"); 53 fos = new FileOutputStream("tomcat2.png"); 54 55 // 1、获取通道 56 inChannel = fis.getChannel(); 57 outChannel = fos.getChannel(); 58 59 // 2、分配指定大小的缓存区 60 ByteBuffer buf = ByteBuffer.allocate(1024); 61 62 // 3、将通道中的数据存入缓冲区中 63 int len; 64 while ((len = inChannel.read(buf)) != -1) { 65 buf.flip(); // 切换成读取数据的模式 66 // 4、将缓冲区中的数据写入通道中 67 outChannel.write(buf); 68 buf.clear(); // 清空缓冲区 69 } 70 } catch (FileNotFoundException e) { 71 e.printStackTrace(); 72 } catch (IOException e) { 73 e.printStackTrace(); 74 } finally { 75 // 5、关闭通道 76 if(outChannel != null) { 77 try { 78 outChannel.close(); 79 } catch (IOException e) { 80 e.printStackTrace(); 81 } 82 } 83 if(inChannel != null) { 84 try { 85 inChannel.close(); 86 } catch (IOException e) { 87 e.printStackTrace(); 88 } 89 } 90 if(fos != null) { 91 try { 92 fos.close(); 93 } catch (IOException e) { 94 e.printStackTrace(); 95 } 96 } 97 if(fis != null) { 98 try { 99 fis.close(); 100 } catch (IOException e) { 101 e.printStackTrace(); 102 } 103 } 104 } 105 } 106 }
示例二:使用直接缓冲区完成文件的复制(内存映射文件)
1 // 2】使用直接缓冲区完成文件的复制(内存映射文件) 2 // 异常需要使用try-catch块处理 3 @Test 4 public void test2() throws IOException { 5 // READ 读文件 6 FileChannel inChannel = FileChannel.open(Paths.get("tomcat.png"), StandardOpenOption.READ); 7 // WRIT 写文件 CREATE_NEW 文件存在就报错,不存在就创建 CREATE 覆盖 8 FileChannel outChannel = FileChannel.open(Paths.get("tomcat2.png"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE); 9 10 // 内存映射文件,直接缓冲区内存在物理内存中 11 MappedByteBuffer inMapappedBuf = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size()); 12 MappedByteBuffer outMapappedBuf = outChannel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size()); 13 14 // 直接对缓冲区进行数据的读写操作 15 byte[] dst = new byte[1024]; 16 ByteBuffer buf; 17 int len = 0; 18 while (inMapappedBuf.hasRemaining()){ 19 len = dst.length > inMapappedBuf.remaining() ? inMapappedBuf.remaining() : dst.length; 20 // System.out.println(len); 21 inMapappedBuf.get(dst, 0, len) ; 22 outMapappedBuf.put(dst, 0, len); 23 } 24 }
2.2、通道之间数据传输
在Java NIO中,如果两个通道中有一个是FileChannel,那你可以直接将数据从一个channel传输到另外一个channel。
transferFrom()
FileChannel的transferFrom()方法可以将数据从源通道传输到FileChannel中
1 // 3】通道之间数据传输(也是直接缓冲区方式) 2 // 异常需要使用try-catch块处理 3 @Test 4 public void test3() throws IOException { 5 // READ 读文件 6 FileChannel inChannel = FileChannel.open(Paths.get("tomcat.png"), StandardOpenOption.READ); 7 // WRIT 写文件 CREATE_NEW 文件存在就报错,不存在就创建 CREATE 存在覆盖,不存在创建 8 FileChannel outChannel = FileChannel.open(Paths.get("tomcat2.png"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE); 9 10 // inChannel.transferTo(0, inChannel.size(), outChannel); 11 outChannel.transferFrom(inChannel, 0, inChannel.size()); 12 13 outChannel.close(); 14 inChannel.close(); 15 }