FileChannel的基本用法
- [1. 获取FileChannel对象](#1. 获取FileChannel对象)
- [2. 从FileChannel中读取数据(channel.read(buffer)))](#2. 从FileChannel中读取数据(channel.read(buffer))))
- [3. 向FileChannel中写入数据(channel.write(buffer))](#3. 向FileChannel中写入数据(channel.write(buffer)))
- [4. 关闭FileChannel](#4. 关闭FileChannel)
- [5. transferTo](#5. transferTo)
-
- [5.1 文件不超过2g,可以一次传输完成](#5.1 文件不超过2g,可以一次传输完成)
- [5.2 文件超过2g,则多次传输](#5.2 文件超过2g,则多次传输)
FileChannel只能工作在【阻塞模式】下;(不能配合selector工作在非阻塞模式)
1. 获取FileChannel对象
FileChannel对象不能直接创建,需通过输入输出流来获取FileChannel对象
- FileInputStream的getChannel()方法;
- FileOutputStream的getChannel()方法;
- RandomAccessFile的getChannel()方法;
channel虽然是数据读写的双向通道,但是
- 通过FileInputStream获取的FileChannel只能读
- 通过FileOutputStream获取的FileChannel只能写
- 通过RandomAccessFile获取的FileChannel对象,根据构造RandomAccessFile对象时的读写模式决定;
示例:
java
public static void main(String[] args) {
try(FileInputStream fileInputStream = new FileInputStream("text.txt")) {
FileChannel channel = fileInputStream.getChannel();
// 从内存中分配一个buffer,大小为10个字节
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
// ByteBuffer中写入数据
byteBuffer.put("abc".getBytes(StandardCharsets.UTF_8));
// 试图从Buffer中读取数据,写入channel
// 由于当前channel对象是通过FileInputStream获取的,只能读,不能写
int write = channel.write(byteBuffer);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
输出结果:
通过FileInputStream获取的FileChannel只能读,不能写
text
"C:\Program Fis\Java\jdk-1.8\bin\java.exe" ...
Exception in thread "main" java.nio.channels.NonWritableChannelException
at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:201)
at com.weimin.part01_buffer.Buffer01_HelloWorld1.main(Buffer01_HelloWorld1.java:29)
Process finished with exit code 1
2. 从FileChannel中读取数据(channel.read(buffer)))
java
public static void main(String[] args) {
try(FileInputStream fileInputStream = new FileInputStream("text.txt")) {
// 获取文件channel
FileChannel channel = fileInputStream.getChannel();
// 从内存中分配一个buffer,大小为10个字节
ByteBuffer byteBuffer = ByteBuffer.allocate(10);
int read;
// read方法的返回值代表读取到的字节数,当返回值为-1时,代表文件中的所有数据读取完成
// 使用while一直读取,直到全部读取完
while ((read = channel.read(byteBuffer)) != -1) {
System.out.println("读到的字节数:" + read);
// flip方法,切换到【读模式】;flip在这里的意思应该是【浏览 / 快速翻阅】
byteBuffer.flip();
while (byteBuffer.hasRemaining()) {
byte b = byteBuffer.get();
System.out.println((char) b);
}
// buffer中的数据读取完成后,清空buffer的数据
byteBuffer.clear();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
3. 向FileChannel中写入数据(channel.write(buffer))
该例中,在从buffer中读取数据之前,没有调用flip方法,因为通过StandardCharsets.UTF_8.encode方法获得的buffer对象,已经在内部调用过flip方法了。
在while语句中调用fileChannel.write(buffer),是因为write方法并不能保证一次将buffer中的内容全部写入channel
java
public static void main(String[] args) {
try (FileOutputStream fileOutputStream = new FileOutputStream("text.txt");
FileChannel fileChannel = fileOutputStream.getChannel()) {
ByteBuffer buffer = StandardCharsets.UTF_8.encode("write");
while (buffer.hasRemaining()) {
int write = fileChannel.write(buffer);
System.out.println("write bytes: " + write);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
4. 关闭FileChannel
channel在用完之后必须关闭,如果调用了【FileInputStream】、【FileOutputStream】、【RandomAccessFile】的close方法,会间接调用FileChannel的Close方法。
因此,最好使用Java try-with-resources语句。
5. transferTo
将数据从一个文件传输到另一个文件。
- 参数一:从源文件的哪个位置开始传输;
- 参数二:传输到源文件的哪个位置结束;
- 参数三:将源文件的内容传输到哪里。
5.1 文件不超过2g,可以一次传输完成
0表示从源文件的起始位置开始传输;
from.size()表示要传输源文件的所有数据;
to表示数据传输到哪里;
- transferTo方法的效率,比java BIO的效率高,因为底层使用了操作系统的零拷贝进行了优化;
- 缺点是 一次最多传输2g的数据;如果源文件超过2g,只调用一次transferTo方法,目标文件也就只有2g。因此,要想完整的传输2g以上的文件,就需要多次传输。
java
public static void main(String[] args) {
try (FileInputStream fileInputStream = new FileInputStream("from.txt");
FileOutputStream fileOutputStream = new FileOutputStream("to.txt");
FileChannel from = fileInputStream.getChannel();
FileChannel to = fileOutputStream.getChannel()) {
from.transferTo(0, from.size(), to);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
5.2 文件超过2g,则多次传输
java
public static void main(String[] args) {
try (FileInputStream fileInputStream = new FileInputStream("from.txt");
FileOutputStream fileOutputStream = new FileOutputStream("to.txt");
FileChannel from = fileInputStream.getChannel();
FileChannel to = fileOutputStream.getChannel()) {
// size,源文件的大小
long size = from.size();
// left,表示剩余的需要传输的数据的大小,初始值为源文件的size
long left = size;
// left > 0,表示还有内容没有传输完成
while (left > 0) {
// transferTo方法的返回值,表示已经传输的数据的大小
long transferred = from.transferTo(size - left, left, to);
// 剩余需要传输的数据大小 - 已传输的数据的大小
left = left - transferred;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}