先打个小广告,关注辛星教程,我的微信号xinxing0913,该项目源码所在的github地址: https://github.com/xinxing0913/xinxing-nio-guide。
我们在大致了解了Channel和Buffer之后,我们就来写一个文件拷贝的应用吧,其实文件的拷贝很简单,无非就是分为两个部分,即读和写,虽然简单,但是毕竟换了一个套路,我们这里还是分为两个步骤来进行操作。
首先我们来循环读取一个文件,这是一个文本文件,出自朱自清先生的散文《春》,我们来看一下截图:

然后我们就来写一个文件来对它进行读取吧,代码如下所示:
package com.mengzhidu.nio.demo;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* 对文件的循环读取
* 这里我们选择了一个相对大一点的文本文档
* 我们的缓冲区尽量小一些
* 在我们的缓冲区需要使用多次,不要忘记调整position的位置
*/
public class Demo4 {
public static void main(String[] args) throws Exception{
String path = "src/main/resources/demo04.txt";
FileInputStream inputStream = new FileInputStream(path);
// 获取指定文件的通道
FileChannel inputChannel = inputStream.getChannel();
// 我们申请的缓冲区尽量小一些
ByteBuffer byteBuffer = ByteBuffer.allocate(32);
while (true) {
// 重置position
byteBuffer.clear();
// 从通道中读取数据
int n = inputChannel.read(byteBuffer);
// 读取结束,直接跳出
if (n == -1) {
break;
}
// 输出获取到的数据内容
byte[] bytes = byteBuffer.array();
System.out.println("读取的长度:" + n + " 内容:" + new String(bytes, 0, n ));
}
// 不要忘记关闭通道
inputChannel.close();
}
}
这里我们每次都读取32个字节,需要说明的是,在每次读取数据前都清理一下缓冲区是有必要的,它可以让我们的position置零,对于数据的读取,我们这里是获取原始字节数组,并且手工截断的方式。然后我们来看一下具体的执行效果吧,如下所示:

需要说明的是,上面的代码中,我们会看到一些乱码,这是因为我们单个字节无法有效的表达出这个汉字,还有些地方会有换行符。
对于文件的读取,我们知道了如何通过一个缓冲区去读取,那么我们改如何写呢?其实思路是类似的,我们可以通过FileOutputStream来获取一个FileChannel,当然也可以通过别的方式,比如RandAccessFile来获取,当然也可以通过一些其他的类来获取。
在这里着重介绍一下Buffer的flip()操作,好像很多朋友对此感到迷惑,其实我们看一下它的源码,发现只有四行,即
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
它是什么意思呢,假设原来是读模式,已经读到缓冲区的position这个位置了,那么接下来再读的话,肯定是从position+1的位置开始的,但是我们现在要进行写操作了,我们需要把0到position这部分数据写出去,我们就得重新设置一下新的position和limit的值了,也就是会有上述的代码。如果对这部分还不太理解的话,我们可以参照下面的代码进一步理解。
我们的代码内容如下:
package com.mengzhidu.nio.demo;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* 这里我们是在一个循环中依次对文件进行读写操作
* 当然我们获取FileChannel也可以使用其他方式,比如RandAccessFile类来获取
* 下面就是我们对应的代码范例:
* FileChannel readChannel = new RandomAccessFile(readPath, "r").getChannel();
* FileChannel writeChannel = new RandomAccessFile(readPath, "rw").getChannel();
*/
public class Demo5 {
public static void main(String[] args) throws Exception{
String readPath = "src/main/resources/demo04.txt";
String writePath = "src/main/resources/demo05.txt";
// 获取指定文件的通道
FileChannel readChannel = new FileInputStream(readPath).getChannel();
FileChannel writeChannel = new FileOutputStream(writePath).getChannel();
// 我们申请的缓冲区尽量小一些
ByteBuffer byteBuffer = ByteBuffer.allocate(32);
while (true) {
// 清空数据并且读取数据
byteBuffer.clear();
int n = readChannel.read(byteBuffer);
System.out.println("n的值:" + n);
// 读取结束,直接跳出
if (n <= 0) {
break;
}
// 进行反转,把limit设置为position,把position置为0,把mark置为-1
byteBuffer.flip();
// 把数据从缓冲区写入通道
writeChannel.write(byteBuffer);
}
System.out.println("操作完成");
// 关闭读写通道
readChannel.close();
writeChannel.close();
}
}
来简单介绍一下上述代码,在整个for循环中,我们通过flip进行了一次反转,我们告诉buffer,我之前进行的是读操作,现在要进行写操作啦,请把对应的数据调整一下,然后我们调用writeChannel把数据写入即可。
我们来看一下执行结果吧:
...省略前面的一系列值...
n的值:32
n的值:32
n的值:32
n的值:32
n的值:1
n的值:-1
操作完成
当然我们的数据也被成功的写入了,看一下效果吧:

至此,我们使用nio进行文件的拷贝就完成了,有什么问题可以在下面留言奥。
网友评论