概述:
由 java.nio.channels 包定義的。
Channel 表示 IO 源與目標打開的連接。
Channel 類似於傳統的“流”。
只不過 Channel本身不能直接訪問數據, Channel 只能與Buffer 進行交互!
接下來,本人來展示下 Channel 是 如何進行數據的傳輸 的:
那么,本人來講解下 Channel 的 常用API:
常用API:
現在,本人來展示下 Channel 接口的實現類的對象的獲取手段:
獲取 Channel:
手段1: 獲取通道的一種方式是對支持通道的對象調用getChannel() 方法:
- public FileChannel getChannel()
支持通道的類如下:
本地I/O:
FileInputStream
FileOutputStream
RandomAccessFile網絡 I/O:
DatagramSocket
Socket
ServerSocket
手段2:
使用 Files 類的靜態方法 newByteChannel()方法 獲取字節通道。
- static SeekableByteChannel newByteChannel(Path path, OpenOption... options)
打開或創建一個文件,返回一個可尋址的字節通道存取文件。- static SeekableByteChannel newByteChannel(Path path, Set options, FileAttribute... attrs)
打開或創建一個文件,返回一個可尋址的字節通道存取文件
手段3:
通過 Channel 接口 的靜態方法 open()方法 打開並返回指定通道。
- static FileChannel open(Path path, OpenOption... options)
在我們獲得了 Channel 接口 的實現類的對象之后,進行信息的傳輸:
數據讀寫:
- public void write(ByteBuffer dst):
將 Buffer 中數據寫入 Channel- public void read(ByteBuffer dst):
從 Channel 讀取數據到 Buffer
判斷 可用性:
- void close()
關閉此通道- boolean isOpen()
告訴是否這個通道是打開的
那么,在本篇博文中,本人主要講解下 Channel 接口實現類中的 FileChannel類:
FileChannel 類:
獲得對象的手段在上文中已經講解過了,本人就不講解這個類的構造方法了
(一般不會有要求通過構造方法來獲取Channel的對象)
那么,本人來展示下這個類的常用API:
常用API:
- int read(ByteBufferdst):
從Channel中讀取數據到ByteBuffer- long read(ByteBuffer[] dsts):
將Channel中的數據“分散”到ByteBuffer[]- int write(ByteBuffer src):
將ByteBuffer中的數據寫入到Channel- long write(ByteBuffer[] srcs):
將ByteBuffer[]中的數據“聚集”到Channel- long position():
返回此通道的文件位置- FileChannel position(long p):
設置此通道的文件位置- long size():
返回此通道的文件的當前大小- FileChannel truncate(long s):
將此通道的文件截取為給定大小- void force(boolean metaData):
強制將所有對此通道的文件更新寫入到存儲設備中
那么,現在,本人來分別展示下使用 FileChannel 類 和 非直接緩沖區/直接緩沖區 來進行文件的復制操作:
使用展示:
首先是 Channel 接口 和 非直接緩沖區 版本:
package edu.youzg.about_nio.core;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class FileCopy {
public static void main(String[] args) throws IOException {
FileInputStream in = new FileInputStream("plantsVSzombies.mp4");
FileOutputStream out = new FileOutputStream("copyViewFile.mp4");
//獲取通道
FileChannel inChannel = in.getChannel();
FileChannel outChannel = out.getChannel();
//面向通道,和緩沖區來復制文件
//分配一個非直接緩沖區
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
//讀寫文件
while (inChannel.read(byteBuffer) != -1){
//切換讀取模式
byteBuffer.flip();
//寫數據
outChannel.write(byteBuffer);
//清空緩沖區
byteBuffer.clear();
}
//釋放資源
in.close();
out.close();
inChannel.close();
outChannel.close();
}
}
首先,本人展示下源文件的信息:
現在,本人來展示下 生成文件 的信息:
那么,本人再來展示下使用 FileChannel 類 和 直接緩沖區 進行文件復制:
package edu.youzg.about_nio.core;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileCopy {
public static void main(String[] args) throws IOException {
//通過文件通道的靜態方法,打開讀寫通道
//參1:通過Paths獲取源文件的路徑
//參2:操作模式 StandardOpenOption.READ 讀取模式
//打開讀取文件的通道
FileChannel in = FileChannel.open(Paths.get("copyViewFile.mp4"), StandardOpenOption.READ);
//打開寫入的通道 模式要讀還要寫 StandardOpenOption.CREATE 意思是文件不存在就創建,如果存在就覆蓋
//StandardOpenOption.CREATE_NEW 意思是文件不存在就創建,如果存在就報錯
FileChannel out = FileChannel.open(Paths.get("copyViewFile2.mp4"), StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//操作內存映射文件(也就是這個緩沖區在物理內存中)
MappedByteBuffer inByteBuffer = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
MappedByteBuffer outByteBuffer = out.map(FileChannel.MapMode.READ_WRITE, 0, in.size());
//直接對緩沖區進行讀寫操作
byte[] bytes = new byte[inByteBuffer.limit()];
inByteBuffer.get(bytes);
outByteBuffer.put(bytes);
//釋放資源
in.close();
out.close();
}
}
現在,本人來展示下 生成文件 的信息:
現在,本人來介紹一下通道的轉換性質:
通道的轉換性質 主要依靠如下兩個方法實現:
轉換性質 API:
- public abstract long transferFrom(ReadableByteChannel src, long position, long count):
將字節從給定的可讀字節通道(即:輸入通道)傳輸到這個通道的文件中- public abstract long transferTo(long position, long count, WritableByteChannel target):
將字節從這通道的文件給出到可寫字節通道(即:輸出通道)
那么,現在,本人來通過這兩個方法,實現下 文件復制 操作:
使用展示:
package edu.youzg.about_nio.core;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileCopy {
public static void main(String[] args) throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("copyViewFile2.mp4"), StandardOpenOption.READ);
FileChannel outChannel1 = FileChannel.open(Paths.get("copyViewFile3.mp4"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
FileChannel outChannel2 = FileChannel.open(Paths.get("copyViewFile4.mp4"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//站在輸入通道的角度
inChannel.transferTo(0,inChannel.size(),outChannel1);
//站在輸出通道的角度
outChannel2.transferFrom(inChannel,0,inChannel.size());
}
}
現在,本人來展示下 生成文件 的信息:
那么,可以看到,文件的復制成功了!
在本篇博文的最后,本人講解下一個很重要的思想 —— 分散 (Scatter) 和 聚集 (Gather):
分散(Scatter) 和 聚集(Gather):
簡介:
所謂的分散和聚集,
就是 分散讀取、聚集寫入
那么,本人現在來解釋下這兩個名詞:
分散讀取(Scattering Reads)
:
從 Channel 中讀取的數據“分散”到多個Buffer緩沖區中聚集寫入(Gathering Writes)
:
將多個 Buffer緩沖區 中的數據“聚集”到 Channel
本人現在通過兩張圖來展示下這兩個知識點:
分散讀取(Scattering Reads):
(注意:按照緩沖區的順序,從Channel中讀取的數據依次將Buffer填滿)
聚集寫入(Gathering Writes):
(注意:按照緩沖區的順序,寫入position和limit之間的數據到Channel)
使用展示:
那么,現在,本人來利用這兩個知識點,來實現下文件的復制操作:
package edu.youzg.about_nio.core;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class FileCopy {
public static void main(String[] args) throws IOException {
FileChannel inChannel = FileChannel.open(Paths.get("copyViewFile4.mp4"), StandardOpenOption.READ);
FileChannel outChanle = FileChannel.open(Paths.get("copyViewFile5.mp4"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
//分配多個緩沖區(緩沖區要分配得足夠)
ByteBuffer byteBuffer1 = ByteBuffer.allocate(1024*2);
ByteBuffer byteBuffer2 = ByteBuffer.allocate(1024*1024*20);
//定義一個數組
ByteBuffer[] byteBuffers={byteBuffer1,byteBuffer2};
//分散
inChannel.read(byteBuffers);
//聚集
for (ByteBuffer byteBuffer : byteBuffers) {
byteBuffer.flip();//轉換成讀取模式
}
//寫出數據
outChanle.write(byteBuffers);
//釋放資源
outChanle.close();
inChannel.close();
}
}
現在,本人來展示下生成的文件:
可以看到,文件復制成功了!
其實對於 NIO
而言,在網絡通信過程中,
對於 Channel
,我們只需要掌握其 獲取Channel,以及 和 Buffer
、Selector
搭配使用 的方法即可!