Channel基本使用——FileChannel类和内存映射的使用

本文详细介绍了Java NIO中的FileChannel,包括如何创建、从FileChannel读取和写入数据,以及利用内存映射文件高效复制大文件的方法。FileChannel不支持非阻塞模式,并且通常通过InputStream、OutputStream或RandomAccessFile获得。通过FileChannel.read()和FileChannel.write()方法进行数据传输,对于大文件复制,内存映射文件提供了更快的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Channel

基本上,所有的 IO 在NIO 中都从一个Channel 开始。Channel 有点象流。 数据可以从Channel读到Buffer中,也可以从Buffer 写到Channel中。这里有个图示:
在这里插入图片描述
JAVA NIO中的一些主要Channel的实现:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

其中后三个主要用于网络编程,第一个是对文件的操作。

FileChannel类

Java NIO中的FileChannel是一个连接到文件的通道。可以通过文件通道读写文件。
FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下。

一、创建FileChannel

主要有两种创建方式:
第一种:使用一个InputStream、OutputStream或RandomAccessFile来获取一个FileChannel实例。
第二种:JDK1.7之后才能使用, FileChannel.open()方法。

//InputStream、OutputStream或RandomAccessFile,创建FileChannel
RandomAccessFile raf = new RandomAccessFile("D:\\Java\\gp.txt", "r");
FileChannel channel = raf.getChannel();

FileInputStream fis = new FileInputStream("D:\\Java\\gp.txt");
FileChannel channel1 = fis.getChannel();

FileChannel channel2 = new FileOutputStream("D:\\Java\\gp.txt").getChannel();

//FileChannel.open()方法创建Channel
//Paths.get()获取路径,StandardOpenOption.READ表示读的模式
FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\gp.jpg"), StandardOpenOption.READ);

//CREATE,如果文件存在就不创建,不存在就创建
FileChannel writeChanne1 = FileChannel.open(Paths.get("D:\\Java\\gp.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

//APPEND表示追加写入
FileChannel fileChannel = FileChannel.open(Paths.get("D:\\Java\\gp.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);

二、从FileChannel读取数据

//读取数据,但是隐含“写”的过程,另外如果读到流的结尾,可能返回0或-1,
int read(ByteBuffer dst);
long read(ByteBuffer[] dsts);
long read(ByteBuffer[] dsts, int offset, int length);
long read(ByteBuffer dst, long position);
long size(); 

读取的方式是将硬盘的数据读出来,然后放到(写入put)缓冲区,所以隐含写的过程,最后要从缓冲区读取数据,这时候必须 Flip() 转读取模式。

public class FileChannleDemo {
    public static void main(String[] args) throws IOException {
        //创建RandomAccessFile
        RandomAccessFile raf = new RandomAccessFile("D:\\Java\\gp4.txt", "r");
        //创建管道
        FileChannel channel = raf.getChannel();
        //读取
        ByteBuffer buf = ByteBuffer.allocate(1024);

        /**
         * 该方法将硬盘的数据读出来,然后放到缓冲区
         * 隐含写的过程
         * 然后变成读模式,把数据取出
         */
        while (channel.read(buf) > 0) {
            buf.flip();
            System.out.println(new String(buf.array(), 0, buf.limit()));
            buf.clear();
        }
        channel.close();
        raf.close();
    }
}

三、向FileChannel写数据

使用FileChannel.write()方法向FileChannel写数据,该方法的参数是一个Buffer。

注意:fileChannel.write()是在while循环中调用的。因为无法保证write()方法一次能向fileChannel写完所有字节,因此需要重复调用write()方法,直到Buffer中已经没有尚未写入通道的字节。

public class FileChannelWrite {
    public static void main(String[] args) throws IOException {
        FileChannel fileChannel = FileChannel.open(Paths.get("D:\\Java\\Demo.txt"), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.APPEND);
        //写入
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        for (int i = 0; i < 10; i++) {
            buffer.put("\r\n好好学习".getBytes());
            buffer.flip();
            fileChannel.write(buffer);
            buffer.clear();
        }
        fileChannel.close();
    }
}

复制

public class FileChannelCopy {
    public static void main(String[] args) throws IOException {
    	//读取通道
        FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\gp.jpg"), StandardOpenOption.READ);
        //写入通道
        FileChannel writeChanne1 = FileChannel.open(Paths.get("D:\\Java\\gp2.jpg"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        //读取写入
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (readChannel.read(buffer) > 0) {
            buffer.flip();
            writeChanne1.write(buffer);
            buffer.clear();
        }
        readChannel.close();
        writeChanne1.close();
    }
}

四、使用内存映射文件复制大文件

内存映射文件属于直接缓冲区,直接在内存开辟空间,如果文件超过2G,需要分多个文件映射,速度快。

文件整体复制

/**
* 2G以下文件的整体复制
*/
public class Demo3 {
    public static void main(String[] args) throws IOException {
        FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\blog.wmv"), StandardOpenOption.READ);
        FileChannel writeChannel = FileChannel.open(Paths.get("D:\\Java\\Blog\\shipin.wmv"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);
		//读取通道获取内存映射map
		//第一个参数是模式,由FileChannel静态方法调用,再调用只读模式
		//后面参数是文件的读取起始位置和文件的大小
        MappedByteBuffer map = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, readChannel.size());
        
        writeChannel.write(map);
        readChannel.close();
        writeChannel.close();

        System.out.println("复制完毕");
    }
}

大文件拆分复制
其实就是更改文件读取的起始位置和文件的大小,注意,最后的参数是【文件大小】,而不是文件的末尾,计算要准确。

public class Demo3 {
    public static void main(String[] args) throws IOException {
        FileChannel readChannel = FileChannel.open(Paths.get("D:\\Java\\Blog\\shipin.wmv"), StandardOpenOption.READ);
        FileChannel writeChannel = FileChannel.open(Paths.get("D:\\Java\\Blog\\shipin2.wmv"), StandardOpenOption.WRITE, StandardOpenOption.CREATE);

        MappedByteBuffer map1 = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, 1024 * 1024 * 40);
        MappedByteBuffer map2 = readChannel.map(FileChannel.MapMode.READ_ONLY, 1024 * 1024 * 40, 1024 * 1024 * 40);
        MappedByteBuffer map3 = readChannel.map(FileChannel.MapMode.READ_ONLY, 1024 * 1024 * 80, readChannel.size() - 1024 * 1024 * 80);
        writeChannel.write(map1);
        writeChannel.write(map2);
        writeChannel.write(map3);

        readChannel.close();
        writeChannel.close();

        System.out.println("复制完毕");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值