美文网首页
辛星2018年nio教程第三篇:通过nio进行文件拷贝实例

辛星2018年nio教程第三篇:通过nio进行文件拷贝实例

作者: 辛星0913 | 来源:发表于2018-02-05 00:16 被阅读0次

先打个小广告,关注辛星教程,我的微信号xinxing0913,该项目源码所在的github地址: https://github.com/xinxing0913/xinxing-nio-guide

我们在大致了解了Channel和Buffer之后,我们就来写一个文件拷贝的应用吧,其实文件的拷贝很简单,无非就是分为两个部分,即读和写,虽然简单,但是毕竟换了一个套路,我们这里还是分为两个步骤来进行操作。

首先我们来循环读取一个文件,这是一个文本文件,出自朱自清先生的散文《春》,我们来看一下截图:


4.jpg

然后我们就来写一个文件来对它进行读取吧,代码如下所示:

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置零,对于数据的读取,我们这里是获取原始字节数组,并且手工截断的方式。然后我们来看一下具体的执行效果吧,如下所示:


5.jpg

需要说明的是,上面的代码中,我们会看到一些乱码,这是因为我们单个字节无法有效的表达出这个汉字,还有些地方会有换行符。

对于文件的读取,我们知道了如何通过一个缓冲区去读取,那么我们改如何写呢?其实思路是类似的,我们可以通过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
操作完成

当然我们的数据也被成功的写入了,看一下效果吧:


6.jpg

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

相关文章

网友评论

      本文标题:辛星2018年nio教程第三篇:通过nio进行文件拷贝实例

      本文链接:https://www.haomeiwen.com/subject/lfwmzxtx.html