Java Files工具类:文件操作工具方法详解(2万字保姆级教程)

一、Files工具类概述

1.1 Files工具类简介

Files类是Java NIO.2 API中提供的一个实用工具类,位于java.nio.file包中。它包含大量静态方法,用于对文件系统中的文件进行各种操作,如创建、删除、复制、移动、读取和写入文件等。

Files工具类的主要特点包括:

完全由静态方法组成,无需实例化
提供了丰富的文件操作方法
支持原子操作和文件属性操作
与Path接口紧密结合使用
提供了更现代的替代方案来替代传统的File类

1.2 Files与传统File类的对比

下表展示了Files类与传统File类的主要区别:

特性 Files类 File类
所属包 java.nio.file java.io
主要配合类型 Path接口 自身就是类
方法类型 静态方法 实例方法
异常处理 抛出IOException 返回boolean表示成功/失败
符号链接处理 明确支持,可配置 有限支持
原子操作 支持原子操作 不支持
文件属性操作 提供丰富API 有限支持
性能 通常更高 相对较低
功能丰富度 更丰富的功能集 功能相对有限

1.3 Files类的核心优势

Files类相比传统File类具有多方面优势:

更丰富的功能:提供了更多高级文件操作,如文件属性访问、目录流处理等
更好的异常处理:通过抛出异常而非返回布尔值,提供更详细的错误信息
原子操作支持:某些操作如移动文件可以保证原子性
符号链接处理:明确支持符号链接及其配置
性能优化:底层实现通常比传统IO更高效
与现代API集成:与NIO.2的其他组件如Path、FileSystem等无缝集成

二、Files工具类基础操作

2.1 文件检查操作

Files类提供了一系列方法用于检查文件状态:

2.1.1 检查文件是否存在
Path path = Paths.get("example.txt");
boolean exists = Files.exists(path);
System.out.println("文件是否存在: " + exists);

方法说明:

exists(Path path, LinkOption... options):检查文件是否存在
参数:

path:要检查的文件路径
options:可选参数,用于指定如何处理符号链接,常用LinkOption.NOFOLLOW_LINKS表示不跟随符号链接

返回值:boolean,表示文件是否存在

2.1.2 检查文件是否可读/可写/可执行
Path path = Paths.get("example.txt");
boolean readable = Files.isReadable(path);
boolean writable = Files.isWritable(path);
boolean executable = Files.isExecutable(path);

System.out.println("可读: " + readable);
System.out.println("可写: " + writable);
System.out.println("可执行: " + executable);

方法说明:

isReadable(Path path):检查文件是否可读
isWritable(Path path):检查文件是否可写
isExecutable(Path path):检查文件是否可执行
这些方法不会抛出异常,而是返回false如果检查失败

2.1.3 检查文件类型
Path filePath = Paths.get("example.txt");
Path dirPath = Paths.get("docs");

boolean isFile = Files.isRegularFile(filePath);
boolean isDir = Files.isDirectory(dirPath);
boolean isLink = Files.isSymbolicLink(filePath);

System.out.println("是普通文件: " + isFile);
System.out.println("是目录: " + isDir);
System.out.println("是符号链接: " + isLink);

方法说明:

isRegularFile(Path path, LinkOption... options):检查是否是普通文件
isDirectory(Path path, LinkOption... options):检查是否是目录
isSymbolicLink(Path path):检查是否是符号链接

2.2 文件创建与删除

2.2.1 创建文件
Path newFile = Paths.get("newfile.txt");
try {
            
    // 创建文件,如果文件已存在会抛出FileAlreadyExistsException
    Path createdFile = Files.createFile(newFile);
    System.out.println("文件创建成功: " + createdFile);
} catch (FileAlreadyExistsException e) {
            
    System.out.println("文件已存在: " + newFile);
} catch (IOException e) {
            
    System.err.println("创建文件失败: " + e.getMessage());
}

方法说明:

createFile(Path path, FileAttribute<?>... attrs):创建新文件
参数:

path:要创建的文件路径
attrs:可选的文件属性,用于设置创建时的属性

如果文件已存在,抛出FileAlreadyExistsException

2.2.2 创建临时文件
try {
            
    // 在默认临时目录创建临时文件
    Path tempFile1 = Files.createTempFile("prefix", ".suffix");
    System.out.println("临时文件1: " + tempFile1);
    
    // 在指定目录创建临时文件
    Path tempDir = Paths.get("mytemp");
    Path tempFile2 = Files.createTempFile(tempDir, "app", ".tmp");
    System.out.println("临时文件2: " + tempFile2);
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

createTempFile(String prefix, String suffix, FileAttribute<?>... attrs):在默认临时目录创建临时文件
createTempFile(Path dir, String prefix, String suffix, FileAttribute<?>... attrs):在指定目录创建临时文件
临时文件名将自动生成,保证唯一性

2.2.3 创建目录
Path newDir = Paths.get("new_directory");
try {
            
    // 创建单个目录
    Path createdDir = Files.createDirectory(newDir);
    System.out.println("目录创建成功: " + createdDir);
    
    // 创建多级目录
    Path multiLevelDir = Paths.get("parent/child/grandchild");
    Path createdMultiDir = Files.createDirectories(multiLevelDir);
    System.out.println("多级目录创建成功: " + createdMultiDir);
} catch (FileAlreadyExistsException e) {
            
    System.out.println("目录已存在: " + newDir);
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

createDirectory(Path dir, FileAttribute<?>... attrs):创建单个目录
createDirectories(Path dir, FileAttribute<?>... attrs):创建多级目录(包括所有不存在的父目录)

2.2.4 删除文件或目录
Path fileToDelete = Paths.get("file_to_delete.txt");
Path dirToDelete = Paths.get("dir_to_delete");

try {
            
    // 删除文件
    Files.delete(fileToDelete);
    System.out.println("文件删除成功");
    
    // 删除目录(必须为空)
    Files.delete(dirToDelete);
    System.out.println("目录删除成功");
} catch (NoSuchFileException e) {
            
    System.out.println("文件/目录不存在: " + e.getFile());
} catch (DirectoryNotEmptyException e) {
            
    System.out.println("目录不为空,无法删除");
} catch (IOException e) {
            
    e.printStackTrace();
}

// 安全删除,文件不存在不会抛出异常
boolean deleted = Files.deleteIfExists(fileToDelete);
System.out.println("文件是否被删除: " + deleted);

方法说明:

delete(Path path):删除文件或空目录,如果不存在抛出NoSuchFileException
deleteIfExists(Path path):安全删除,文件不存在返回false而不抛出异常

2.3 文件复制与移动

2.3.1 复制文件
Path source = Paths.get("source.txt");
Path target = Paths.get("target.txt");

try {
            
    // 基本复制
    Files.copy(source, target);
    System.out.println("文件复制成功");
    
    // 带选项的复制
    Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
    System.out.println("文件复制成功(覆盖已存在文件)");
} catch (FileAlreadyExistsException e) {
            
    System.out.println("目标文件已存在,未指定REPLACE_EXISTING选项");
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

copy(Path source, Path target, CopyOption... options):复制文件
常用选项:

StandardCopyOption.REPLACE_EXISTING:覆盖已存在文件
StandardCopyOption.COPY_ATTRIBUTES:复制文件属性
LinkOption.NOFOLLOW_LINKS:不跟随符号链接

2.3.2 移动/重命名文件
Path source = Paths.get("oldname.txt");
Path target = Paths.get("newname.txt");

try {
            
    // 基本移动/重命名
    Files.move(source, target);
    System.out.println("文件移动/重命名成功");
    
    // 带选项的移动
    Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
    System.out.println("文件移动成功(覆盖已存在文件)");
    
    // 原子移动
    Files.move(source, target, StandardCopyOption.ATOMIC_MOVE);
    System.out.println("文件原子移动成功");
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

move(Path source, Path target, CopyOption... options):移动或重命名文件
常用选项:

StandardCopyOption.REPLACE_EXISTING:覆盖已存在文件
StandardCopyOption.ATOMIC_MOVE:保证移动操作的原子性

2.3.3 复制目录

Files.copy()方法也可以用于复制目录,但需要注意:

只复制空目录,不复制目录内容
要复制目录及其内容需要递归操作

public static void copyDirectory(Path sourceDir, Path targetDir) throws IOException {
            
    // 首先创建目标目录
    Files.createDirectories(targetDir);
    
    // 遍历源目录
    try (DirectoryStream<Path> stream = Files.newDirectoryStream(sourceDir)) {
            
        for (Path source : stream) {
            
            Path target = targetDir.resolve(source.getFileName());
            
            if (Files.isDirectory(source)) {
            
                // 如果是目录,递归复制
                copyDirectory(source, target);
            } else {
            
                // 如果是文件,直接复制
                Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
            }
        }
    }
}

三、文件内容读写操作

3.1 小文件读写

3.1.1 读取所有字节/行
Path filePath = Paths.get("example.txt");

// 读取所有字节
try {
            
    byte[] allBytes = Files.readAllBytes(filePath);
    String content = new String(allBytes, StandardCharsets.UTF_8);
    System.out.println("文件内容:
" + content);
} catch (IOException e) {
            
    e.printStackTrace();
}

// 读取所有行
try {
            
    List<String> lines = Files.readAllLines(filePath, StandardCharsets.UTF_8);
    System.out.println("文件行数: " + lines.size());
    for (String line : lines) {
            
        System.out.println(line);
    }
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

readAllBytes(Path path):读取文件所有字节,返回byte[]
readAllLines(Path path, Charset cs):读取文件所有行,返回List

注意:这些方法适合小文件,大文件可能导致内存不足

3.1.2 写入字节/行
Path filePath = Paths.get("output.txt");

// 写入字节
try {
            
    String content = "Hello, Files工具类!
第二行内容";
    Files.write(filePath, content.getBytes(StandardCharsets.UTF_8));
    System.out.println("字节写入成功");
} catch (IOException e) {
            
    e.printStackTrace();
}

// 写入行
try {
            
    List<String> lines = Arrays.asList("第一行", "第二行", "第三行");
    Files.write(filePath, lines, StandardCharsets.UTF_8);
    System.out.println("行写入成功");
    
    // 追加模式
    Files.write(filePath, lines, StandardCharsets.UTF_8, 
               StandardOpenOption.APPEND);
    System.out.println("内容追加成功");
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

write(Path path, byte[] bytes, OpenOption... options):写入字节
write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options):写入行
常用OpenOption:

StandardOpenOption.CREATE:如果不存在则创建
StandardOpenOption.CREATE_NEW:创建新文件,如果存在则失败
StandardOpenOption.TRUNCATE_EXISTING:如果存在则截断
StandardOpenOption.APPEND:追加模式
StandardOpenOption.WRITE:写访问
StandardOpenOption.READ:读访问

3.2 大文件流式读写

对于大文件,应该使用流式读写以避免内存问题

3.2.1 使用BufferedReader读取
Path largeFile = Paths.get("largefile.txt");

try (BufferedReader reader = Files.newBufferedReader(largeFile, StandardCharsets.UTF_8)) {
            
    String line;
    while ((line = reader.readLine()) != null) {
            
        // 处理每一行
        System.out.println(line);
    }
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

newBufferedReader(Path path, Charset cs):创建BufferedReader
适合逐行处理大文本文件

3.2.2 使用BufferedWriter写入
Path outputFile = Paths.get("output_large.txt");

try (BufferedWriter writer = Files.newBufferedWriter(outputFile, 
        StandardCharsets.UTF_8, StandardOpenOption.CREATE)) {
            
    for (int i = 0; i < 10000; i++) {
            
        writer.write("这是第 " + i + " 行
");
    }
    System.out.println("大文件写入完成");
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

newBufferedWriter(Path path, Charset cs, OpenOption... options):创建BufferedWriter
适合逐行写入大文本文件

3.2.3 使用InputStream/OutputStream
// 二进制文件读取
Path binaryFile = Paths.get("image.jpg");
try (InputStream in = Files.newInputStream(binaryFile)) {
            
    byte[] buffer = new byte[1024];
    int bytesRead;
    while ((bytesRead = in.read(buffer)) != -1) {
            
        // 处理读取的数据
    }
} catch (IOException e) {
            
    e.printStackTrace();
}

// 二进制文件写入
Path outputBinary = Paths.get("copy.jpg");
try (OutputStream out = Files.newOutputStream(outputBinary, 
        StandardOpenOption.CREATE)) {
            
    byte[] data = new byte[1024];
    // 填充data...
    out.write(data);
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

newInputStream(Path path, OpenOption... options):创建输入流
newOutputStream(Path path, OpenOption... options):创建输出流
适合处理二进制文件或需要更细粒度控制的场景

3.3 随机访问文件

Files类也支持随机访问文件操作:

Path logFile = Paths.get("app.log");

// 使用SeekableByteChannel进行随机访问
try (SeekableByteChannel channel = Files.newByteChannel(logFile, 
        StandardOpenOption.READ, StandardOpenOption.WRITE)) {
            
    
    // 移动到文件末尾
    channel.position(channel.size());
    
    // 写入日志条目
    String logEntry = "New log entry at " + new Date() + "
";
    ByteBuffer buffer = ByteBuffer.wrap(logEntry.getBytes(StandardCharsets.UTF_8));
    channel.write(buffer);
    
    // 回到文件开头读取
    channel.position(0);
    buffer.clear();
    while (channel.read(buffer) > 0) {
            
        buffer.flip();
        System.out.print(StandardCharsets.UTF_8.decode(buffer));
        buffer.clear();
    }
} catch (IOException e) {
            
    e.printStackTrace();
}

方法说明:

newByteChannel(Path path, OpenOption... options):创建SeekableByteChannel
支持随机访问和读写操作
比RandomAccessFile更现代和灵活的API

四、高级文件操作

4.1 文件属性操作

Files类提供了丰富的API来访问和修改文件属性

4.1.1 基本属性
Path file = Paths.get("example.txt");

try {
            
    // 文件大小
    long size = Files.size(file);
    System.out.println("文件大小: " + size + " 字节");
    
    // 最后修改时间
    FileTime lastModified = Files.getLastModifiedTime(file);
    System.out.println("最后修改时间: " + lastModified);
    
    // 文件所有者
    UserPrincipal owner = Files.getOwner(file);
    System.out.println("文件所有者: " + owner.getName());
    
    // 文件权限
    Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(file);
    System.out.println("文件权限: " + permissions);
} catch (IOException e) {
            
    e.printStackTrace();
}
4.1.2 修改属性
Path file = Paths.get("example.txt");

try {
            
    // 设置最后修改时间
    FileTime newTime = FileTime.fromMillis(System.currentTimeMillis());
    Files.setLastModifiedTime(file, newTime);
    
    // 设置文件所有者(需要权限)
    UserPrincipalLookupService lookupService = 
        file.getFileSystem().getUserPrincipalLookupService();
    UserPrincipal newOwner = lookupService.lookupPrincipalByName("username");
    Files.setOwner(file, newOwner);
    
    // 设置文件权限(POSIX系统)
    Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r-----");
    Files.setPosixFilePermissions(file, perms);
} catch (IOException e) {
            
    e.printStackTrace();
}
4.1.3 文件属性视图

Files类支持通过不同的视图访问文件属性:

视图类型 描述
BasicFileAttributeView 基本文件属性,所有文件系统都支持
DosFileAttributeView DOS相关属性(如隐藏、存档等)
PosixFileAttributeView POSIX系统属性(如用户、组、权限等)
FileOwnerAttributeView 文件所有者信息
AclFileAttributeView 访问控制列表(ACL)
UserDefinedAttributeView 用户自定义属性
Path file = Paths.get("example.txt");

// 获取基本文件属性视图
BasicFileAttributeView basicView = 
    Files.getFileAttributeView(file, BasicFileAttributeView.class);
try {
            
    BasicFileAttributes attrs = basicView.readAttributes();
    System.out.println("创建时间: " + attrs.creationTime());
    System.out.println("最后访问时间: " + attrs.lastAccessTime());
    System.out.println("最后修改时间: " + attrs.lastModifiedTime());
    System.out.println("是目录: " + attrs.isDirectory());
    System.out.println("是普通文件: " + attrs.isRegularFile());
    System.out.println("是符号链接: " + attrs.isSymbolicLink());
    System.out.println("大小: " + attrs.size());
} catch (IOException e) {
            
    e.printStackTrace();
}

4.2 目录操作

4.2.1 遍历目录
Path dir = Paths.get(".");

// 简单遍历
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
            
    for (Path entry : stream) {
            
        System.out.println(entry.getFileName());
    }
} catch (IOException e) {
            
    e.printStackTrace();
}

// 带过滤器的遍历
try (DirectoryStream<Path> stream = 
        Files.newDirectoryStream(dir, "*.{java,class}")) {
            
    for (Path entry : stream) {
            
        System.out.println("Java文件: " + entry.getFileName());
    }
} catch (IOException e) {
            
    e.printStackTrace();
}
4.2.2 递归遍历目录
Path startDir = Paths.get(".");

// 使用walkFileTree递归遍历
try {
            
    Files.walkFileTree(startDir, new SimpleFileVisitor<Path>() {
            
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
                throws IOException {
            
            System.out.println("访问文件: " + file);
            return FileVisitResult.CONTINUE;
        }
        
        @Override
        public FileVisitResult preVisitDirectory(Path dir, 
                BasicFileAttributes attrs) throws IOException {
            
            System.out.println("进入目录: " + dir);
            return FileVisitResult.CONTINUE;
        }
        
        @Override
        public FileVisitResult visitFileFailed(Path file, IOException exc) 
                throws IOException {
            
            System.err.println("访问文件失败: " + file);
            return FileVisitResult.CONTINUE;
        }
        
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) 
                throws IOException {
            
            System.out.println("离开目录: " + dir);
            return FileVisitResult.CONTINUE;
        }
    });
} catch (IOException e) {
            
    e.printStackTrace();
}
4.2.3 查找文件
Path startDir = Paths.get(".");

// 查找所有.java文件
try {
            
    Files.find(startDir, Integer.MAX_VALUE, 
            (path, attrs) -> path.toString().endsWith(".java"))
        .forEach(System.out::println);
} catch (IOException e) {
            
    e.printStackTrace();
}

4.3 文件监控

Files类可以与WatchService API结合使用来监控文件系统变化:

Path dir = Paths.get(".");

try {
            
    WatchService watcher = FileSystems.getDefault().newWatchService();
    
    // 注册感兴趣的事件类型
    dir.register(watcher, 
            StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_DELETE,
            StandardWatchEventKinds.ENTRY_MODIFY);
    
    System.out.println("开始监控目录: " + dir);
    
    while (true) {
            
        WatchKey key;
        try {
            
            key = watcher.take();
        } catch (InterruptedException e) {
            
            return;
        }
        
        for (WatchEvent<?> event : key.pollEvents()) {
            
            WatchEvent.Kind<?> kind = event.kind();
            
            // 处理OVERFLOW事件
            if (kind == StandardWatchEventKinds.OVERFLOW) {
            
                continue;
            }
            
            // 获取文件名
            @SuppressWarnings("unchecked")
            WatchEvent<Path> ev = (WatchEvent<Path>) event;
            Path filename = ev.context();
            
            System.out.println("事件类型: " + kind + ", 文件: " + filename);
        }
        
        // 重置key
        boolean valid = key.reset();
        if (!valid) {
            
            break;
        }
    }
} catch (IOException e) {
            
    e.printStackTrace();
}

五、进阶主题与最佳实践

5.1 文件锁

Files类可以与FileLock结合使用实现文件锁定:

Path file = Paths.get("shared.txt");

try (RandomAccessFile raf = new RandomAccessFile(file.toFile(), "rw");
     FileChannel channel = raf.getChannel()) {
            
    
    // 获取独占锁
    FileLock lock = channel.lock();
    try {
            
        System.out.println("获得文件锁");
        // 执行需要独占访问的操作
        Thread.sleep(5000); // 模拟长时间操作
    } finally {
            
        lock.release();
        System.out.println("释放文件锁");
    }
} catch (IOException | InterruptedException e) {
            
    e.printStackTrace();
}

5.2 内存映射文件

Files类支持内存映射文件操作,提高大文件访问性能:

Path largeFile = Paths.get("large.dat");

try (FileChannel channel = FileChannel.open(largeFile, 
        StandardOpenOption.READ, StandardOpenOption.WRITE)) {
            
    
    // 将文件映射到内存
    MappedByteBuffer buffer = channel.map(
            FileChannel.MapMode.READ_WRITE, 0, channel.size());
    
    // 读取/修改缓冲区内容
    while (buffer.hasRemaining()) {
            
        byte b = buffer.get();
        // 处理字节...
    }
    
    // 修改内容
    buffer.position(0);
    buffer.put("New content".getBytes());
    
} catch (IOException e) {
            
    e.printStackTrace();
}

5.3 文件操作原子性与一致性

Files类中的某些操作提供了原子性保证:

原子移动:使用StandardCopyOption.ATOMIC_MOVE选项的move操作
原子创建createFilecreateTempFile方法保证原子性
原子写入:使用适当的选项组合可以保证写入的原子性

5.4 性能考虑

小文件操作:对于小文件,readAllBytesreadAllLines方法最简单
大文件操作:使用流式API或内存映射文件
缓冲区大小:对于IO操作,适当设置缓冲区大小(通常8KB-32KB)
批量操作:尽可能减少文件系统调用次数

5.5 异常处理最佳实践

文件操作中常见的异常及处理建议:

异常类型 原因 处理建议
NoSuchFileException 文件不存在 检查文件路径,或先调用exists()检查
FileAlreadyExistsException 文件已存在 根据业务逻辑决定:覆盖、跳过或重命名
AccessDeniedException 权限不足 检查文件权限,或提示用户
DirectoryNotEmptyException 目录不为空 先清空目录内容或使用其他策略
IOException 通用IO异常 根据具体错误信息处理,记录详细日志

5.6 跨平台考虑

路径分隔符:使用FileSystems.getDefault().getSeparator()获取平台分隔符
文件系统特性:检查文件系统支持的特性,如符号链接、硬链接等
大小写敏感:注意不同平台对文件名大小写的处理不同
权限模型:Windows和Unix-like系统的权限模型不同

六、实战案例

6.1 文件搜索工具

public class FileSearchUtil {
            
    
    /**
     * 在指定目录及其子目录中搜索包含指定文本的文件
     * @param rootDir 搜索根目录
     * @param textToFind 要查找的文本
     * @return 匹配的文件列表
     */
    public static List<Path> searchFiles(Path rootDir, String textToFind) 
            throws IOException {
            
        List<Path> result = new ArrayList<>();
        
        Files.walkFileTree(rootDir, new SimpleFileVisitor<Path>() {
            
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) 
                    throws IOException {
            
                if (containsText(file, textToFind)) {
            
                    result.add(file);
                }
                return FileVisitResult.CONTINUE;
            }
        });
        
        return result;
    }
    
    private static boolean containsText(Path file, String text) throws IOException {
            
        if (!Files.isRegularFile(file)) {
            
            return false;
        }
        
        try (BufferedReader reader = Files.newBufferedReader(file)) {
            
            String line;
            while ((line = reader.readLine()) != null) {
            
                if (line.contains(text)) {
            
                    return true;
                }
            }
        }
        return false;
    }
    
    public static void main(String[] args) {
            
        try {
            
            Path startDir = Paths.get(".");
            String searchText = "Files工具类";
            
            List<Path> foundFiles = searchFiles(startDir, searchText);
            
            System.out.println("找到 " + foundFiles.size() + " 个匹配文件:");
            foundFiles.forEach(System.out::println);
        } catch (IOException e) {
            
            e.printStackTrace();
        }
    }
}

6.2 文件备份工具

public class FileBackupUtil {
            
    
    /**
     * 备份指定目录到目标位置
     * @param sourceDir 源目录
     * @param backupDir 备份目录
     * @throws IOException 如果备份过程中发生IO错误
     */
    public static void backupDirectory(Path sourceDir, Path backupDir) 
            throws IOException {
            
        // 创建备份目录
        Files.createDirectories(backupDir);
        
        // 遍历源目录
        Files.walk(sourceDir)
            .forEach(source -> {
            
                try {
            
                    Path target = backupDir.resolve(sourceDir.relativize(source));
                    
                    // 如果是目录,创建目录
                    if (Files.isDirectory(source)) {
            
                        Files.createDirectories(target);
                    } 
                    // 如果是文件,复制文件
                    else {
            
                        Files.copy(source, target, 
                                StandardCopyOption.REPLACE_EXISTING,
                                StandardCopyOption.COPY_ATTRIBUTES);
                    }
                } catch (IOException e) {
            
                    throw new UncheckedIOException(e);
                }
            });
    }
    
    public static void main(String[] args) {
            
        try {
            
            Path source = Paths.get("data");
            Path backup = Paths.get("backup_" + 
                    LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
            
            System.out.println("开始备份: " + source + " -> " + backup);
            backupDirectory(source, backup);
            System.out.println("备份完成");
        } catch (IOException e) {
            
            e.printStackTrace();
        }
    }
}

6.3 文件加密工具

public class FileEncryptor {
            
    
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";
    private static final int IV_SIZE = 16;
    
    /**
     * 加密文件
     * @param source 源文件
     * @param target 目标文件
     * @param key 加密密钥
     * @throws IOException IO异常
     * @throws GeneralSecurityException 安全异常
     */
    public static void encrypt(Path source, Path target, SecretKey key) 
            throws IOException, GeneralSecurityException {
            
        // 生成初始化向量
        byte[] iv = new byte[IV_SIZE];
        SecureRandom random = new SecureRandom();
        random.nextBytes(iv);
        IvParameterSpec ivSpec = new IvParameterSpec(iv);
        
        // 初始化加密器
        Cipher cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);
        
        try (InputStream in = Files.newInputStream(source);
             OutputStream out = Files.newOutputStream(target, 
                     StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
            
            
            // 写入IV
            out.write(iv);
            
            // 加密数据
            try (CipherOutputStream cipherOut = new CipherOutputStream(out, cipher)) {
            
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
            
                    cipherOut.write(buffer, 0, bytesRead);
                }
            }
        }
    }
    
    /**
     * 解密文件
     * @param source 源文件
     * @param target 目标文件
     * @param key 解密密钥
     * @throws IOException IO异常
     * @throws GeneralSecurityException 安全异常
     */
    public static void decrypt(Path source, Path target, SecretKey key) 
            throws IOException, GeneralSecurityException {
            
        try (InputStream in = Files.newInputStream(source)) {
            
            // 读取IV
            byte[] iv = new byte[IV_SIZE];
            if (in.read(iv) != IV_SIZE) {
            
                throw new IOException("无效的加密文件格式");
            }
            IvParameterSpec ivSpec = new IvParameterSpec(iv);
            
            // 初始化解密器
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);
            
            try (OutputStream out = Files.newOutputStream(target, 
                     StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
                 CipherInputStream cipherIn = new CipherInputStream(in, cipher)) {
            
                
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = cipherIn.read(buffer)) != -1) {
            
                    out.write(buffer, 0, bytesRead);
                }
            }
        }
    }
    
    public static void main(String[] args) {
            
        try {
            
            // 生成密钥
            KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
            keyGen.init(256);
            SecretKey key = keyGen.generateKey();
            
            Path original = Paths.get("original.txt");
            Path encrypted = Paths.get("encrypted.aes");
            Path decrypted = Paths.get("decrypted.txt");
            
            // 加密文件
            encrypt(original, encrypted, key);
            System.out.println("文件加密完成");
            
            // 解密文件
            decrypt(encrypted, decrypted, key);
            System.out.println("文件解密完成");
            
            // 验证
            byte[] originalBytes = Files.readAllBytes(original);
            byte[] decryptedBytes = Files.readAllBytes(decrypted);
            System.out.println("解密验证: " + Arrays.equals(originalBytes, decryptedBytes));
        } catch (Exception e) {
            
            e.printStackTrace();
        }
    }
}

七、总结

Java的Files工具类提供了强大而灵活的文件操作API,涵盖了从基础到高级的各种文件操作需求。通过本教程,我们详细探讨了:

基础操作:文件检查、创建、删除、复制和移动
内容读写:小文件和大文件的读写策略,包括流式处理和内存映射
高级特性:文件属性操作、目录遍历、文件监控等
进阶主题:文件锁、原子操作、性能优化和跨平台考虑
实战案例:文件搜索、备份和加密工具的实现

Files类的设计体现了现代Java API的特点:

方法命名清晰一致
异常处理明确
支持链式调用
与NIO.2其他组件良好集成
提供丰富的选项和灵活性

在实际开发中,应根据具体需求选择合适的API:

对于简单操作,使用便捷方法如readAllByteswrite
对于大文件或性能敏感场景,使用流式API或内存映射
需要原子性保证时,使用适当的选项
跨平台应用注意文件系统差异

通过合理使用Files工具类,可以编写出健壮、高效且可维护的文件操作代码,满足各种复杂的业务需求。

喜欢的点个已关注,想了解更多的可以已关注微信公众号 “Eric的技术杂货库” ,提供更多的干货以及资料下载保存!

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容