NIO-文件编程

本文详细介绍了Java NIO中的FileChannel,包括其工作模式、读写操作、关闭、位置控制、强制写入以及两个Channel之间的数据传输。此外,还讲解了Path和Files类在处理路径和文件操作如查找、创建、拷贝、删除等方面的应用。

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

NIO-文件编程

FileChannel

工作模式

FileChannel只能在阻塞模式下工作,所以无法搭配Selector

获取

不能直接打开 FileChannel,必须通过 FileInputStream、FileOutputStream 或者 RandomAccessFile 来获取 FileChannel,它们都有 getChannel 方法

  • 通过 FileInputStream 获取的 channel 只能读
  • 通过 FileOutputStream 获取的 channel 只能写
  • 通过 RandomAccessFile 是否能读写根据构造 RandomAccessFile 时的读写模式决定
读取

通过 FileInputStream 获取channel,通过read方法将数据写入到ByteBuffer中

read方法的返回值表示读到了多少字节,若读到了文件末尾则返回-1

int readBytes = channel.read(buffer);

可根据返回值判断是否读取完毕

while(channel.read(buffer) > 0) {
    // 进行对应操作
    ...
}
写入

因为channel的一次写能力是有上限,所以 write 方法并不能保证一次将 buffer 中的内容全部写入 channel。必须需要按照以下规则进行写入

// 通过hasRemaining()方法查看缓冲区中是否还有数据未写入到通道中
while(buffer.hasRemaining()) {
	channel.write(buffer);
}
关闭

通道需要close,一般情况通过try-with-resource进行关闭,最好使用以下方法获取strema以及channel,避免某些原因使得资源未被关闭

public class TestChannel {
    public static void main(String[] args) throws IOException {
        try (FileInputStream fis = new FileInputStream("stu.txt");
             FileOutputStream fos = new FileOutputStream("student.txt");
             FileChannel inputChannel = fis.getChannel();
             FileChannel outputChannel = fos.getChannel()) {
            
            // 执行对应操作
            ...
                
        }
    }
}
位置

position

channel也拥有一个保存读取数据位置的属性,即position

long pos = channel.position();

可以通过position(int pos)设置channel中position的值

long newPos = ...;
channel.position(newPos);

设置当前位置时,如果设置为文件的末尾

  • 这时读取会返回 -1
  • 这时写入,会追加内容,但要注意如果 position 超过了文件末尾,再写入时在新内容和原末尾之间会有空洞(00)
强制写入

操作系统出于性能的考虑,会将数据缓存,不是立刻写入磁盘,而是等到缓存满了以后将所有数据一次性的写入磁盘。可以调用 force(true) 方法将文件内容和元数据(文件的权限等信息)立刻写入磁盘

两个Channel传输数据

FileChannel
package com.Netty.FileChannel;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

/**
 * @Author:nioliu
 * @DATE: 2021/9/6  14:22
 */
public class TestFileChannelTransferTo {
    public static void main(String[] args) {
        // 一个输入流(输入到内存), 一个输出流(输出到磁盘)
        try (final FileChannel from = new FileInputStream("muguangzhicheng.qsv").getChannel();
             final FileChannel to = new FileOutputStream("to.qsv").getChannel()) {

            // 效率高. 底层会利用操作系统的零拷贝进行优化(每次最多传2G数据):使用一个大文件(一个电影)进行测试
            for (long left = from.size(); left > 0; ) {
                final long l = from.transferTo(from.size() - left, left, to);// 返回的是传输的字节数
                System.out.println(l);
                left -= l;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
Path(对路径的操作:生成/遍历等等)
  • Path 用来表示文件路径
  • Paths 是工具类,用来获取 Path 实例
Path source = Paths.get("1.txt"); // 相对路径 不带盘符 使用 user.dir 环境变量来定位 1.txt

Path source = Paths.get("d:\\1.txt"); // 绝对路径 代表了  d:\1.txt 反斜杠需要转义

Path source = Paths.get("d:/1.txt"); // 绝对路径 同样代表了  d:\1.txt

Path projects = Paths.get("d:\\data", "projects"); // 代表了  d:\data\projectsCopy
  • . 代表了当前路径
  • .. 代表了上一级路径

例如目录结构如下

d:
	|- data
		|- projects
			|- a
			|- bCopy

代码

Path path = Paths.get("d:\\data\\projects\\a\\..\\b");
System.out.println(path);
System.out.println(path.normalize()); // 正常化路径 会去除 . 以及 ..Copy

输出结果为

d:\data\projects\a\..\b
d:\data\projects\b
Files(对文件的具体操作:读写复制删除等等)
查找

检查文件是否存在

Path path = Paths.get("helloword/data.txt");
System.out.println(Files.exists(path));
创建

创建一级目录

Path path = Paths.get("helloword/d1");
Files.createDirectory(path);
  • 如果目录已存在,会抛异常 FileAlreadyExistsException
  • 不能一次创建多级目录,否则会抛异常 NoSuchFileException

创建多级目录用

Path path = Paths.get("helloword/d1/d2");
Files.createDirectories(path);
拷贝及移动

拷贝文件

Path source = Paths.get("helloword/data.txt");
Path target = Paths.get("helloword/target.txt");

Files.copy(source, target);
  • 如果文件已存在,会抛异常 FileAlreadyExistsException

如果希望用 source 覆盖掉 target,需要用 StandardCopyOption 来控制

Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

移动文件

Path source = Paths.get("helloword/data.txt");
Path target = Paths.get("helloword/data.txt");

Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
  • StandardCopyOption.ATOMIC_MOVE 保证文件移动的原子性
删除

删除文件

Path target = Paths.get("helloword/target.txt");

Files.delete(target);Copy
  • 如果文件不存在,会抛异常 NoSuchFileException

删除目录

Path target = Paths.get("helloword/d1");

Files.delete(target);Copy
  • 如果目录还有内容,会抛异常 DirectoryNotEmptyException

遍历目录

可以使用Files工具类中的walkFileTree(Path, FileVisitor)方法,其中需要传入两个参数

  • Path:文件起始路径

  • FileVisitor:文件访问器,

    使用访问者模式

    • 接口的实现类

      SimpleFileVisitor

      有四个方法

      • preVisitDirectory:访问目录前的操作
      • visitFile:访问文件的操作
      • visitFileFailed:访问文件失败时的操作
      • postVisitDirectory:访问目录后的操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值