实战
复制一张图片
public static void main(String[] args) throws IOException {
String fileName = parentPath + "\\104.jpg";
Pipe pipe = Pipe.open();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
try (WritableByteChannel writableByteChannel = Channels.newChannel(Channels.newOutputStream(pipe.sink()))){
FileChannel channel = new FileInputStream(fileName).getChannel();
channel.transferTo(0, new File(fileName).length(), writableByteChannel);
}
File file = new File(parentPath + "\\b.jpg");
ReadableByteChannel readableByteChannel = pipe.source();
FileChannel fileChannel = new FileOutputStream(file).getChannel();
while (readableByteChannel.read(byteBuffer) > 0) {
byteBuffer.flip();
fileChannel.write(byteBuffer);
byteBuffer.clear();
}
}
private static String parentPath = "D:\\github\\zip";
private static String path = parentPath + "\\104.jpg";
上面这样写是因为想熟悉一下NIO,Java NIO管道是2个线程之间的单向数据连接。Pipe
有一个源通道和一个sink通道.
sink负责写通道,source负责读通道
为啥这么做
transferTo
首先是transferTo,
我们可以看到这里并没有使用ByteBuffer
进行数据传输,而是使用了transferTo
的方法。这个方法是将两个通道进行直连。
This method is potentially much more efficient than a simple loop
* that reads from this channel and writes to the target channel. Many
* operating systems can transfer bytes directly from the filesystem cache
* to the target channel without actually copying them.
这是源码上的描述文字,大概意思就是使用transferTo
的效率比循环一个Channel
读取出来然后再循环写入另一个Channel
好。操作系统能够直接传输字节从文件系统缓存到目标的Channel
中,而不需要实际的copy
阶段。
copy阶段就是从内核空间转到用户空间的一个过程
缓冲器介绍
我们可以看到一半的copy是从内核空间复制到用户空间,而transferTo就是开辟直接缓存。还可以开辟一个物理内存映射,MappedByteBuffer。
sink和source相互阻塞
Java NIO 管道是2个线程之间的单向数据连接。Pipe有一个source通道和一个sink通道。其中source通道用于读取数据,sink通道用于写入数据。可以看到源码中的介绍,大概意思就是写入线程会阻塞至有读线程从通道中读取数据。如果没有数据可读,读线程也会阻塞至写线程写入数据。直至通道关闭。
Whether or not a thread writing bytes to a pipe will block until another
thread reads those bytes
我们可以使用CompletableFuture将sink和source进行异步分开操作。
原理
单纯的复制
WritableByteChannel writableByteChannel = Channels.newChannel(zipOut);
try (FileChannel fileChannel = new FileInputStream(fileToZip).getChannel()) {
fileChannel.transferTo(0, fileToZip.length(), writableByteChannel);
}
很简单就实现了。