FileChannel
Channel,通道或管道,与IO中的流很相似,常与Buffer一起连用。即将数据由管道读入到缓冲区或者将数据由缓冲区写入到管道中。Channel大致分为以下几种:
- FileChannel
- DatagramChannel
- SocketChanne
- ServerSocketChannel
本次主要结合api介绍下FileChannel的常见用法
FileChannel,从文件中读写数据。可以由输入输出流、RandomAccessFile等类来获取
1.FileChannel的几种常见创建方式:
RandomAccessFile accessFile = new RandomAccessFile("1.txt", "rw");
FileChannel channel = accessFile.getChannel();
上面类中的mode是”rw”,常见还有以下几种:
“r” 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
“rw” 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
“rws” 打开以便读取和写入,对于 “rw”,还要求对文件的内容或元数据的每个更新都同步写入到基础存储设备。
“rwd” 打开以便读取和写入,对于 “rw”,还要求对文件内容的每个更新都同步写入到基础存储设备。
这里具体采用哪种mode,要根据具体的情况分析,如需要将通道数据读入到缓冲区,则只需要r权限即可。而若需要将缓冲区数据写入通道,则需要rw权限,所以以实际情况为准。
FileInputStream fileInputStream = new FileInputStream("1.txt");
FileChannel channel = fileInputStream.getChannel();
FileOutputStream fileOutputStream = new FileOutputStream("1.txt");
FileChannel channel = fileOutputStream.getChannel();
2.常用方法介绍(以ByteBuffer为例):
read(ByteBuffer):将文件中数据由通道读入到缓冲区中。
write(ByteBuffer):将数据由缓冲区写入到通道对应的文件中。
size():返回通道对应文件的大小
truncate(long size):截取通道中size个字节的内容,将0-size的数据写入到原文件,将size+1-channel.size()之间的数据删除掉
transferFrom(ReadableByteChannel src,long position, long count)从某个通道中复制position--count个字节的数据到另一个通道
transferTo(long position, long count,WritableByteChannel target)将某个通道中复制position--count个字节的数据到另一个通道
3.常见用法:
备注:下面采用的是相对路径,且1.txt初始数据是good good study good
- 从通道中读取数据至缓冲区,并显示出来
RandomAccessFile randomAccessFile = new RandomAccessFile("1.txt", "r");
FileChannel channel = randomAccessFile.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = channel.read(buffer);
System.out.println(buffer);
while (read != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.println("缓冲区元素:" + (char) buffer.get());
}
// 缓冲区满之后,清空缓冲区 再次读取文件
buffer.clear();
read = channel.read(buffer);
}
channel.close();
randomAccessFile.close();
运行结果:
java.nio.HeapByteBuffer[pos=20 lim=1024 cap=1024]
缓冲区元素:g
缓冲区元素:o
缓冲区元素:o
缓冲区元素:d
缓冲区元素:
缓冲区元素:g
缓冲区元素:o
缓冲区元素:o
缓冲区元素:d
缓冲区元素:
缓冲区元素:s
缓冲区元素:t
缓冲区元素:u
缓冲区元素:d
缓冲区元素:y
缓冲区元素:
缓冲区元素:g
缓冲区元素:o
缓冲区元素:o
缓冲区元素:d
- 从数据从缓冲区写入到通道中,若原始文件中有数据,这种方式会将原数据覆盖,下面介绍追加的方式
RandomAccessFile accessFile = new RandomAccessFile("1.txt", "rw");
FileChannel channel = accessFile.getChannel();
String data = " good good study lalala";
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(data.getBytes());
buffer.flip();
while (buffer.hasRemaining()) {
channel.write(buffer);
}
channel.close();
accessFile.close();
- 从数据从缓冲区写入到通道中(追加的方式)
try {
RandomAccessFile accessFile = new RandomAccessFile("1.txt", "rw");
FileChannel channel = accessFile.getChannel();
String data = " good good study lalala";
// ByteBuffer buffer = ByteBuffer.wrap(data.getBytes());
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = channel.read(buffer);
buffer.put(data.getBytes());
buffer.limit(buffer.position());
buffer.position(read);
while (buffer.hasRemaining()) {
channel.write(buffer);
}
channel.close();
accessFile.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
从上面可以看出,由于原文件中的数据和要写入文件的数据均写入了缓冲区中,只需要控制缓冲区写入文件的position的位置和limit即可。
- 从数据从缓冲区写入到通道中(追加的方式),该思路是借助channel.position()方法,在指定的位置写入元素,代码如下:
RandomAccessFile accessFile = new RandomAccessFile("1.txt", "rw");
FileChannel channel = accessFile.getChannel();
// channel.size() 返回通道中文件的大小
channel.position(channel.size());
String data = " good good study lalala";
// 使用wrap方法之后,position和limit都自动发生了变化,不用人为调用flip方法
ByteBuffer buffer = ByteBuffer.wrap(data.getBytes());
// buffer.flip();
while (buffer.hasRemaining()) {
System.out.println("写入之前:" + buffer);
channel.write(buffer);
System.out.println("写入之后:" + buffer);
}
channel.close();
accessFile.close();
- Channel-to-Channel传输
RandomAccessFile fromFile = new RandomAccessFile("1.txt", "r");
FileChannel fromChannel = fromFile.getChannel();
RandomAccessFile toFile = new RandomAccessFile("2.txt", "rw");
FileChannel toChannel = toFile.getChannel();
// 写入到to通道中数据的大小 最多是from通道文件的大小
toChannel.transferFrom(fromChannel, 0, fromChannel.size());
ByteBuffer buffer = ByteBuffer.allocate(1024);
int read = toChannel.read(buffer);
while (read != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
System.out.print((char) buffer.get());
}
System.out.println();
buffer.clear();
read = toChannel.read(buffer);
}
toChannel.close();
toFile.close();
fromChannel.close();
fromFile.close();
从一个通道写入数据到另一个通道,若另一个通道对应的文件不存在,则在写入成功的时候,会创建该文件,并将数据写入到该文件中。这点与OutPutStream非常像。为了展示效果,特地将toChannel中的数据读到缓冲区中。运行结果如下:
good good study good
RandomAccessFile fromFile = new RandomAccessFile("1.txt", "r");
FileChannel fromChannel = fromFile.getChannel();
WritableByteChannel newChannel = Channels.newChannel(System.out);
// 写入到to通道中数据的大小 最多是from通道文件的大小
fromChannel.transferTo(0, fromChannel.size(), newChannel);
fromChannel.close();
fromChannel.close();
fromFile.close();
其实,个人认为transferTo和transferFrom这两个方法在使用方面很像。
为了展示效果未使用FileChannel,而是选择将fromChannel的数据拷贝到控制台。运行效果如下:
good good study good
后记:若您在阅读当中发现错误,请指出,谢谢!