NIO之FileChannel的用法

FileChannel 读取数据到 Buffer
(读取数据可能会出现中文乱码,如下防止乱码)

public static void main(String[] args) throws IOException {
		RandomAccessFile raFile = new RandomAccessFile("d:\\TEST\\666.txt", "rw");
		FileChannel fChannel = raFile.getChannel();
		ByteBuffer byteBuffer = ByteBuffer.allocate(32);
		CharBuffer charBuffer = CharBuffer.allocate(32);
		while ((fChannel.read(byteBuffer)) != -1) {read()
//方法返回的 int 值表示了有多少字节被读到了 Buffer 中。如果返回-1,表示到了文件末尾。
			byteBuffer.flip();// 切换buffer从读模式到写模式
			StandardCharsets.UTF_8.newDecoder().decode(byteBuffer, charBuffer, true);// 以utf8编码转换ByteBuffer到CharBuffer(**防止中文数据中文乱码**)
			charBuffer.flip();// 切换buffer从读模式到写模式
			while (charBuffer.hasRemaining()) {
				System.out.print(charBuffer.get());
			}
			byteBuffer.compact();//(该方法清除被读取的缓冲区,没有读取的会放在缓冲区首部)
			charBuffer.clear();
		}
 
		raFile.close();
	}

buffer向FileChannel 写数据

public static void main(String[] args) throws IOException {
		 //打开FileChannel
        RandomAccessFile randomAccessFile = new RandomAccessFile("d:\\TEST\\666.txt","rw");
        FileChannel channel = randomAccessFile.getChannel();

        //创建buffer对象
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String newData = "写入数据12346566test";

        //写入内容
        buffer.put(newData.getBytes());
        buffer.flip();

        // FileChannel.write()是在 while 循环中调用的。因为无法保证 write()方法一次能向 FileChannel写入多少字节,因此需要重复调用 write()方法,直到 Buffer 中已经没有尚未写入通道的字节。
        while (buffer.hasRemaining()) {
            channel.write(buffer);
        }

        //关闭
        channel.close();

	}

其它方法:

Position
当读取或写入FileChannel时,需要在特定position执行。你可以通过调用position()方法来获得FileChannel的当前position。
你还可以通过调用position(long pos)来设置FileChannel的position。

long pos = channel.position();
channel.position(pos + 66);

如果你设置的position超过了文件的大小,并且尝试从Channel读取数据,则会返回-1代表文件结尾。

如果你设置的position超过了文件的大小,并且尝试往Channel写入数据,文件会自动扩张至能放下position以及写入的数据。这个可能导致"file hole",即磁盘上的物理文件在写入的数据中存在漏洞(即中间有一段完全没有任何数据)。

Size
FileChannel的size()方法返回这个Channel连接的文件大小。如下:

long fileSize = channel.size();

FileChannel Truncate
通过调用FileChannel.truncate()方法,你可以truncate一个文件。当你truncate一个文件,你会把其截断为指定的长度。如下:

channel.truncate(1024);

这个例子将文件截断为1024字节。

Force
FileChannel.force()方法会将Channel里面还未写入的数据全部刷新到磁盘。操作系统可能会将数据缓存在内存里以提升性能,因此我们无法保证你写入Channel的数据都被写到了磁盘,直到你调用force()方法。

force()方法有一个boolean类型的参数,代表是否将文件元数据(比如权限等)也刷新到磁盘。

以下是刷新数据以及元数据到磁盘的例子:

channel.force(true);

通道之间的数据传输:
如果两个通道中有一个是 FileChannel,那你可以直接将数据从一个 channel 传输到
另外一个 channel
transferFrom()

public static void main(String[] args) throws IOException {
		 //打开FileChannel
        RandomAccessFile randomAccessFile = new RandomAccessFile("d:/TEST/666.txt","rw");
        RandomAccessFile aFile = new
        		RandomAccessFile("d:\\TEST\\666.txt", "rw");
        		FileChannel fromChannel = aFile.getChannel();
        		RandomAccessFile bFile = new
        		RandomAccessFile("d:\\TEST\\777.txt", "rw");
        		FileChannel toChannel = bFile.getChannel();
        		long position = 0;
        		long count = fromChannel.size();
        		toChannel.transferFrom(fromChannel, position, count);
        		aFile.close();
        		bFile.close();
        		System.out.println("完成");
	}

方法的输入参数 position 表示从 position 处开始向目标文件写入数据,count 表示最
多传输的字节数。如果源通道的剩余空间小于 count 个字节,则所传输的字节数要小
于请求的字节数。此外要注意,在 SoketChannel 的实现中,SocketChannel 只会传
输此刻准备好的数据(可能不足 count 字节)。因此,SocketChannel 可能不会将请
求的所有数据(count 个字节)全部传输到 FileChannel
transferTo()
transferTo()方法将数据从 FileChannel 传输到其他的 channel 中。

public static void main(String[] args) throws IOException {

		RandomAccessFile aFile = new
				RandomAccessFile("d:\\TEST\\666.txt", "rw");
		FileChannel fromChannel = aFile.getChannel();
		RandomAccessFile bFile = new
				RandomAccessFile("d:\\TEST\\777.txt", "rw");
		FileChannel toChannel = bFile.getChannel();
		long position = 0;
		long count = fromChannel.size();
		fromChannel.transferTo(position, count, toChannel);
		aFile.close();
		bFile.close();
		System.out.println("完成");
	}

Scatter/Gather
Java NIO 开始支持 scatter/gather,scatter/gather 用于描述从 Channel 中读取或
者写入到 Channel 的操作。
分散(scatter)从 Channel 中读取是指在读操作时将读取的数据写入多个 buffer 中。
因此,Channel 将从 Channel 中读取的数据“分散(scatter)”到多个 Buffer 中。
聚集(gather)写入 Channel 是指在写操作时将多个 buffer 的数据写入同一个
Channel,因此,Channel 将多个 Buffer 中的数据“聚集(gather)”后发送到
Channel。
scatter / gather 经常用于需要将传输的数据分开处理的场合,例如传输一个由消息头
和消息体组成的消息,你可能会将消息体和消息头分散到不同的 buffer 中,这样你可
以方便的处理消息头和消息体。
Scattering Reads
Scattering Reads 是指数据从一个 channel 读取到多个 buffer 中。如

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);
注意 buffer 首先被插入到数组,然后再将数组作为 channel.read() 的输入参数。
read()方法按照 buffer 在数组中的顺序将从 channel 中读取的数据写入到 buffer,当
一个 buffer 被写满后,channel 紧接着向另一个 buffer 中写。
Scattering Reads 在移动下一个 buffer 前,必须填满当前的 buffer,这也意味着它
不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,
消息头必须完成填充(例如 128byte),Scattering Reads 才能正常工作。

Gathering Writes
Gathering Writes 是指数据从多个 buffer 写入到同一个 channel。如下图描述:

ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = { header, body };
channel.write(bufferArray);
buffers 数组是 write()方法的入参,write()方法会按照 buffer 在数组中的顺序,将数
据写入到 channel,注意只有 position 和 limit 之间的数据才会被写入。因此,如果
一个 buffer 的容量为 128byte,但是仅仅包含 58byte 的数据,那么这 58byte 的数
据将被写入到 channel 中。因此与 Scattering Reads 相反,Gathering Writes 能较
好的处理动态消息。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值