FileChannel
获取方法
- 通过 FileInputStream 获取 channel 只能读操作
- 通过FileOutputStream 获取 channel 只能写操作
- 通过 RandomAccessFile 是否能读取根据构造的 RandomAccessFile 读写模式决定
// RandomAccessFile 方法中的第二个参数就是 决定是否 可读写操作
// 可读
FileChannel channel = new RandomAccessFile("words.txt", "r").getChannel()
// 可写
FileChannel channel = new RandomAccessFile("words.txt", "w").getChannel()
// 可读写
FileChannel channel = new RandomAccessFile("words.txt", "rw").getChannel()
FileChannel from = new FileInputStream("H:\\AE+2020最新版(6月份).rar").getChannel();
FileChannel to = new FileOutputStream("H:\\AE+2020.rar").getChannel())
操作方法
- read 读方法
- write 写方法
- close 关闭方法
- position 获取当前的指针位置
- size 获取文件的大小
- transferTo 拷贝数据
代码案例
public static void fileApi() {
try {
FileChannel from = new FileInputStream("data.txt").getChannel();
FileChannel to = new FileOutputStream("to.txt").getChannel();
ByteBuffer buffer = ByteBuffer.allocate(2);
long size = from.size();
log.debug("文件的大小:{}",size);
for (long remaining = size;remaining>0;){
// 从 from 通道内 读取数据 并写入 buffer
int read = from.read(buffer);
// 查看 from 当前指针指向的位置
long position = from.position();
log.debug("当前的位置:{}",position);
// 将 buffer 切换为读模式
buffer.flip();
// buffer 中的数据读取并写入 to 通道
int write = to.write(buffer);
// 清空 buffer中 所有的数据
buffer.clear();
//剩余数据量 = 总数 - 已经读取的数据量
remaining = remaining - read;
}
from.close();
to.close();
// todo 效率高,底层会利用操作系统的 零拷贝 进行优化,传输数据有上限
for (long residue = size; residue > 0;) {
log.debug("以读数:{},剩余数:{}",size - residue, residue);
residue -= from.transferTo(size - residue, residue, to);
}
} catch (IOException e) {
e.printStackTrace();
}
}
打印结果

Path 和 Paths yi'ji
- 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\projects
File 和 Files
常用方法
- exists()检查文件是否存在
- createDirectory() 创建一级目录
- createDirectories() 创建多级目录用
- copy() 拷贝文件
- move() 移动文件
- delete() 删除文件/删除目录
代码案列
// exists
Path path = Paths.get("helloword/data.txt");
System.out.println(Files.exists(path));
// createDirectory
Path path = Paths.get("helloword/d1");
Files.createDirectory(path);
//如果目录已存在,会抛异常 FileAlreadyExistsException
//不能一次创建多级目录,否则会抛异常 NoSuchFileException
// createDirectories
Path path = Paths.get("helloword/d1/d2");
Files.createDirectories(path);
// copy
Path source = Paths.get("helloword/data.txt");
Path target = Paths.get("helloword/target.txt");
Files.copy(source, target);
// 如果希望用 source 覆盖掉 target,需要用 StandardCopyOption 来控制
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
// move
Path source = Paths.get("helloword/data.txt");
Path target = Paths.get("helloword/data.txt");
// StandardCopyOption.ATOMIC_MOVE 保证文件移动的原子性
Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
// delete 文件
Path target = Paths.get("helloword/target.txt");
Files.delete(target);
// 如果文件不存在,会抛异常 NoSuchFileException
// delete 目录
Path target = Paths.get("helloword/d1");
Files.delete(target);
// 如果目录还有内容,会抛异常 DirectoryNotEmptyException
遍历多级目录以及文件
代码案列
/**
* 遍历多级目录以及其中的文件
* @throws IOException
*/
private static void m1() throws IOException {
//todo 计数器
AtomicInteger dirCount = new AtomicInteger();
AtomicInteger fileCount = new AtomicInteger();
Files.walkFileTree(Paths.get("D:\\java\\jdk8"), new SimpleFileVisitor<>() {
// todo 访问一个目录,在进入之前调用
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
dirCount.incrementAndGet();
log.debug("访问一个目录,在进入之前调用:{}",dir.toString());
return super.preVisitDirectory(dir, attrs);
}
// todo 文件被访问时被调用。该文件的文件属性被传递给这个方法
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if(file.toString().endsWith(".jar")){
fileCount.incrementAndGet();
log.debug("文件被访问时被调用:{}",file.toString());
}
return super.visitFile(file, attrs);
}
// todo 一个目录的所有节点都被访问后调用。遍历时跳过同级目录或有错误发生,Exception会传递给这个方法
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
log.debug("一个目录的所有节点都被访问后调用:{}",dir.toString());
return super.postVisitDirectory(dir, exc);
}
//todo 当文件不能被访问时,此方法被调用。Exception被传递给这个方法。
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
log.debug("当文件不能被访问时,此方法被调用",file.toString());
return super.visitFileFailed(file, exc);
}
});
log.debug("dirCount:{}", dirCount.get());
log.debug("fileCount:{}", fileCount.get());
// todo https://segmentfault.com/a/1190000020778836 博客中有更多的操作方法
}
删除多级目录以及文件
代码案例
/**
* 删除多级目录文件
* @throws IOException
*/
private static void delete() throws IOException {
// todo 删除多级目录文件,只需要在 进入目录之后删除所有文件,删除所有文件之后 出来删除 目录
Files.walkFileTree(Paths.get("D:\\java\\jdk8 - 副本"),new SimpleFileVisitor<Path>(){
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// todo 删除文件
Files.delete(file);
return super.visitFile(file, attrs);
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return super.visitFileFailed(file, exc);
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
// todo 删除目录
Files.delete(dir);
return super.postVisitDirectory(dir, exc);
}
});
}
拷贝多级目录以及文件
代码案例
public static void main(String[] args) throws IOException {
String source = "D:\\gradle-8.11.1";
String target = "D:\\gradle-8.11.1aaa";
/**
* 拷贝 多级目录文件
*/
Files.walk(Paths.get(source)).forEach(path -> {
try {
// todo 将遍历到的目录名 或者 文件名 替换成新的
String targetName = path.toString().replace(source, target);
//如果是目录,则创建
if(Files.isDirectory(path)){
Files.createDirectory(Paths.get(targetName));
}else if(Files.isRegularFile(path)){
Files.copy(path,Paths.get(targetName));
}
} catch (IOException e) {
e.printStackTrace();
}
});
}
更多方法参考
Java 文件操作指南:Path、Paths 与 Files_java paths-优快云博客
https://segmentfault.com/a/1190000020778836