基于Java IO API实现文件复制工具

 更新时间:2026年06月15日 09:24:51   作者:Cache技术分享  
这篇文章主要为大家详细介绍了如何基于Java IO API实现文件复制工具,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下

 Java IO API - Java 文件复制工具

这是一个用 Java 编写的递归文件复制工具,模仿了文件系统复制的操作。它从源目录复制文件到目标目录,并支持指定复制的最大目录层级(-depth)。

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.EnumSet;
import java.util.stream.Stream;
import static java.nio.file.FileVisitResult.CONTINUE;
import static java.nio.file.FileVisitResult.SKIP_SUBTREE;
import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
/**
 * Sample code that copies files recursively 
 * from a source directory to a destination folder.
 * The maximum number of directory levels to copy 
 * is specified after -depth.
 * The number of files copied is printed
 * to standard out.
 * You can execute the application using:
 * @code java Copy . new -depth 4
 */
public class Copy {
    /**
     * A {@code FileVisitor} that finds
     * all files that match the
     * specified pattern.
     */
    public static class Replicator
            extends SimpleFileVisitor<Path> {
        Path source;
        Path destination;
        public Replicator(Path source, Path destination) {
            this.source = source;
            this.destination = destination;
        }
        // Prints the total number of
        // files copied to standard out.
        void done() throws IOException {
            try (Stream<Path> path = Files.list(Paths.get(destination.toUri()))) {
                System.out.println("Number of files copied: "
                        + path.filter(p -> p.toFile().isFile()).count());
            }
        }
        // Copy a file in destination
        @Override
        public FileVisitResult visitFile(Path file,
                                         BasicFileAttributes attrs) {
            System.out.println("Copy file: " + file);
            Path newFile = destination.resolve(source.relativize(file));
            try{
                Files.copy(file,newFile);
            }
            catch (IOException ioException){
                //log it and move
            }
            return CONTINUE;
        }
        // Invoke copy of a directory.
        @Override
        public FileVisitResult preVisitDirectory(Path dir,
                                                 BasicFileAttributes attrs) {
            System.out.println("Copy directory: " + dir);
            Path targetDir = destination.resolve(source.relativize(dir));
            try {
                Files.copy(dir, targetDir, REPLACE_EXISTING, COPY_ATTRIBUTES);
            } catch (IOException e) {
                System.err.println("Unable to create " + targetDir + " [" + e + "]");
                return SKIP_SUBTREE;
            }
            return CONTINUE;
        }
        @Override
        public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
            if (exc == null) {
                Path destination = this.destination.resolve(source.relativize(dir));
                try {
                    FileTime time = Files.getLastModifiedTime(dir);
                    Files.setLastModifiedTime(destination, time);
                } catch (IOException e) {
                    System.err.println("Unable to copy all attributes to: " + destination + " [" + e + "]");
                }
            } else {
                throw exc;
            }
            return CONTINUE;
        }
        @Override
        public FileVisitResult visitFileFailed(Path file,
                                               IOException exc) {
            if (exc instanceof FileSystemLoopException) {
                System.err.println("cycle detected: " + file);
            } else {
                System.err.format("Unable to copy:" + " %s: %s%n", 
                        file, exc);
            }
            return CONTINUE;
        }
    }
    static void usage() {
        System.err.println("java Copy <source> <destination>" +
                " -depth \"<max_level_dir>\"");
        System.exit(-1);
    }
    public static void main(String[] args)
            throws IOException {
        if (args.length < 4 || !args[2].equals("-depth"))
            usage();
        Path source = Paths.get(args[0]);
        Path destination = Paths.get(args[1]);
        int depth = Integer.parseInt(args[3]);
        Replicator walk = new Replicator(source, destination);
        EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
        Files.walkFileTree(source, opts, depth, walk);
        walk.done();
    }
}

功能简介

该程序会:

  • 递归地将源目录中的文件复制到目标目录。
  • 允许通过 -depth 参数指定最大目录层级。
  • 在程序结束时,打印成功复制的文件数。

例如:

$ java Copy . new -depth 4

会把当前目录及其子目录(最多 4 层)中的文件复制到 new 目录。

示例代码结构详解

main()方法:程序入口

public static void main(String[] args) throws IOException {
    if (args.length < 4 || !args[2].equals("-depth"))
        usage();

    Path source = Paths.get(args[0]);
    Path destination = Paths.get(args[1]);
    int depth = Integer.parseInt(args[3]);

    Replicator walk = new Replicator(source, destination);
    EnumSet<FileVisitOption> opts = EnumSet.of(FileVisitOption.FOLLOW_LINKS);
    Files.walkFileTree(source, opts, depth, walk);
    walk.done();
}
  • 输入参数解析:首先检查输入是否符合规范,若不符合则调用 usage() 打印使用说明。
  • 路径和深度解析:从命令行参数中提取源目录、目标目录和最大目录深度。
  • 文件遍历:使用 Files.walkFileTree() 递归遍历源目录,进行复制操作。

Replicator类:文件复制核心逻辑

public static class Replicator extends SimpleFileVisitor<Path> {

该类继承 SimpleFileVisitor<Path> 并重写了几个关键方法来执行复制操作。

构造方法:初始化源路径与目标路径

public Replicator(Path source, Path destination) {
    this.source = source;
    this.destination = destination;
}
  • source:源目录路径;
  • destination:目标目录路径。

visitFile()方法:复制文件

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
    System.out.println("Copy file: " + file);
    Path newFile = destination.resolve(source.relativize(file));
    try {
        Files.copy(file, newFile);
    } catch (IOException ioException) {
        // 错误日志处理
    }
    return CONTINUE;
}
  • 使用 Files.copy() 复制文件到目标路径。
  • source.relativize(file) 计算文件的相对路径,以保持源文件夹结构。
  • 如果复制失败,会捕获并记录异常,但继续执行。

preVisitDirectory()方法:复制目录

@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
    System.out.println("Copy directory: " + dir);
    Path targetDir = destination.resolve(source.relativize(dir));
    try {
        Files.copy(dir, targetDir, REPLACE_EXISTING, COPY_ATTRIBUTES);
    } catch (IOException e) {
        System.err.println("Unable to create " + targetDir + " [" + e + "]");
        return SKIP_SUBTREE;
    }
    return CONTINUE;
}
  • Files.copy() 会复制目录及其属性。
  • 如果目录已存在并且指定了 REPLACE_EXISTING,它将覆盖该目录。
  • 如果复制失败,打印错误信息并跳过该子目录(使用 SKIP_SUBTREE)。

postVisitDirectory()方法:复制目录属性

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    if (exc == null) {
        Path destination = this.destination.resolve(source.relativize(dir));
        try {
            FileTime time = Files.getLastModifiedTime(dir);
            Files.setLastModifiedTime(destination, time);
        } catch (IOException e) {
            System.err.println("Unable to copy all attributes to: " + destination + " [" + e + "]");
        }
    } else {
        throw exc;
    }
    return CONTINUE;
}
  • 在目录复制后,复制目录的最后修改时间等属性。
  • 如果复制失败,打印错误信息。

visitFileFailed()方法:错误处理

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
    if (exc instanceof FileSystemLoopException) {
        System.err.println("Cycle detected: " + file);
    } else {
        System.err.format("Unable to copy: %s: %s%n", file, exc);
    }
    return CONTINUE;
}
  • 处理访问失败的文件。
  • 如果检测到文件系统循环(符号链接等),打印循环错误信息。

done()方法:统计已复制文件数

void done() throws IOException {
    try (Stream<Path> path = Files.list(Paths.get(destination.toUri()))) {
        System.out.println("Number of files copied: "
                + path.filter(p -> p.toFile().isFile()).count());
    }
}

在文件复制完成后,统计并打印成功复制的文件数。

usage()方法:程序使用说明

static void usage() {
    System.err.println("java Copy <source> <destination> -depth \"<max_level_dir>\"");
    System.exit(-1);
}

如果输入参数无效,打印使用说明并退出程序。

扩展思路与练习建议

扩展功能实现方法
复制文件时跳过某些类型visitFile() 方法中加入文件类型判断(如跳过 .txt 文件)
并行复制大文件夹使用 ExecutorService 执行并行文件复制
复制过程中显示进度条使用 System.out.print 打印进度信息
按文件类型筛选复制visitFile() 中加入文件类型过滤,例如只复制 .java 文件
备份和版本控制visitFile() 中实现版本号控制,避免覆盖旧版本

错误处理建议

  • IOException 提供更加详细的日志,例如记录文件复制失败的具体原因,或者保存失败文件列表。
  • visitFileFailed() 方法添加更多的错误类型处理,如权限不足等。

小结

这个 Copy 示例演示了如何使用 Java 的 nio 包进行递归文件复制。通过自定义 FileVisitor 类,你可以实现高效且灵活的文件操作。这个工具可以被扩展和定制化,满足各种实际项目中的需求。

到此这篇关于基于Java IO API实现文件查找和复制工具的文章就介绍到这了,更多相关Java文件查找和复制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring容器中添加bean的5种方式

    Spring容器中添加bean的5种方式

    我们知道平时在开发中使用Spring的时候,都是将对象交由Spring去管理,那么将一个对象加入到Spring容器中,有哪些方式呢,感兴趣的可以了解一下
    2021-07-07
  • idea导入jar包的详细图文教程

    idea导入jar包的详细图文教程

    这篇文章主要给大家介绍了关于idea导入jar包的详细图文教程,文中通过图文将导入的步骤介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2023-03-03
  • IntelliJ IDEA同步代码时版本冲突而产生出的incoming partial文件问题的解决办法

    IntelliJ IDEA同步代码时版本冲突而产生出的incoming partial文件问题的解决办法

    今天小编就为大家分享一篇关于IntelliJ IDEA同步代码时版本冲突而产生出的incoming partial文件问题的解决办法,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2018-10-10
  • 一文带你彻底了解Java8中的Lambda,函数式接口和Stream

    一文带你彻底了解Java8中的Lambda,函数式接口和Stream

    这篇文章主要为大家详细介绍了解Java8中的Lambda,函数式接口和Stream的用法和原理,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-08-08
  • Java如何实现登录token令牌

    Java如何实现登录token令牌

    这篇文章主要介绍了Java如何实现登录token令牌,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-05-05
  • SpringBoot-JWT生成Token和拦截器的使用(访问受限资源)

    SpringBoot-JWT生成Token和拦截器的使用(访问受限资源)

    本文主要介绍了SpringBoot-JWT生成Token和拦截器的使用(访问受限资源),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-05-05
  • java IP归属地功能实现详解

    java IP归属地功能实现详解

    前一阵子抖音和微博开始陆续上了IP归属地的功能,引起了众多热议,有大批在国外的老铁们开始"原形毕露",被定位到国内来,那么IP归属到底是怎么实现的呢?那么网红们的归属地到底对不对呢
    2022-07-07
  • Springboot在有参构造方法类中使用@Value注解取值

    Springboot在有参构造方法类中使用@Value注解取值

    这篇文章主要介绍了Springboot在有参构造方法类中使用@Value注解取值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06
  • Java编写实现登陆窗口

    Java编写实现登陆窗口

    这篇文章主要为大家详细介绍了Java编写实现登陆窗口,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • Java中TypeReference用法详情说明

    Java中TypeReference用法详情说明

    这篇文章主要介绍了Java中TypeReference用法详情说明,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-07-07

最新评论