java 文件拷贝的四种方式

1. java 移动文件的方式有几种?

在 Java 中,可以使用多种方法来移动文件。

//使用 java.nio.file.Files 类的 move() 方法:
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Main {
    public static void main(String[] args) throws Exception {
        Path source = Paths.get("/path/to/source/file.txt");
        Path target = Paths.get("/path/to/target/file.txt");
        Files.move(source, target);
    }
}
//使用 java.io.File 类的 renameTo() 方法:
import java.io.File;

//使用示例
public class Main {
    public static void main(String[] args) {
        File source = new File("/path/to/source/file.txt");
        File target = new File("/path/to/target/file.txt");
        source.renameTo(target);
    }
}

//完整工具类方法封装
	/**
     * 文件移动
     * 源文件不存在renameTo方法返回false但不会报错,所以在工具方法中加入主动检查源文件逻辑;
     * 目标文件如果存在,会被默认覆盖;
     *
     * @param oldPath 文件全路径
     * @param newPath
     */
    public static boolean renameFileTo(String oldPath, String newPath) {
        Log.d(TAG, "renameFileTo: oldPath=" + oldPath + " newPath=" + newPath);
        File source = new File(oldPath);
        if (!source.exists()) {
            Log.d(TAG, "renameFileTo: Source file not exits!");
            return false;
        }
        File target = new File(newPath);
        File targetPatenFile = target.getParentFile();
        if (!targetPatenFile.exists()) {
            boolean mkdirResult = targetPatenFile.mkdirs();
            Log.d(TAG, "renameFileTo: Target parent file not exits! targetPatenFile=" + targetPatenFile);
            Log.d(TAG, "renameFileTo: Target parent file mkdir! mkdirResult=" + mkdirResult);
        }
        boolean result = source.renameTo(target);
        Log.d(TAG, "renameFileTo: result=" + result);
        return result;
    }
//使用 Apache Commons IO 库的 FileUtils.moveFile() 方法:
import org.apache.commons.io.FileUtils;

public class Main {
    public static void main(String[] args) throws Exception {
        File source = new File("/path/to/source/file.txt");
        File target = new File("/path/to/target/file.txt");
        FileUtils.moveFile(source, target);
    }
}

请注意,如果目标文件已经存在,则会覆盖该文件。如果要保留目标文件,可以使用 moveFileToDirectory() 方法将文件移动到目标目录,并使用新的文件名。

import org.apache.commons.io.FileUtils;

public class Main {
    public static void main(String[] args) throws Exception {
        File source = new File("/path/to/source/file.txt");
        File targetDir = new File("/path/to/target/directory");
        String newFileName = "new_file_name.txt";
        FileUtils.moveFileToDirectory(source, targetDir, newFileName);
    }
}

这些方法都可以用来移动文件。使用哪种方法取决于你的需求和偏好。

2. 上面这几种方式,比文件流拷贝效率高吗?

在大多数情况下,使用 java.nio.file.Files.move() 或 Apache Commons IO 库的 FileUtils.moveFile() 方法移动文件都要比使用文件流拷贝文件高效。这是因为,这些方法可以在操作系统层面上直接移动文件,而无需拷贝文件内容。
相比之下,使用文件流拷贝文件需要读取源文件的内容,并将其写入目标文件。这样做需要消耗额外的时间和资源,因此通常不会作为移动文件的首选方法。
但是,如果你希望保留源文件,并且希望在拷贝文件时对其进行某些操作,则可以使用文件流拷贝文件。例如,你可以使用文件流拷贝文件并在拷贝过程中对文件内容进行修改。
总的来说,如果你只是希望移动文件,则使用 java.nio.file.Files.move() 或 Apache Commons IO 库的 FileUtils.moveFile() 方法是最高效的选择。但是,如果你希望对文件进行某些操作,则可以使用文件流拷贝文件。

3. Files.move 和 File.renameTo 有什么区别?

java.nio.file.Files.move() 和 java.io.File.renameTo() 是两种在 Java 中移动文件的方法。它们之间有一些区别:
java.nio.file.Files.move() 方法是在 Java 7 中引入的,它属于 Java 的新 I/O API (NIO.2),使用了基于路径的方法来处理文件系统。相比之下,java.io.File.renameTo() 方法是在早期版本的 Java 中引入的,它使用了基于文件的方法来处理文件系统。
java.nio.file.Files.move() 方法提供了更多的选项,可以指定文件移动时的行为。例如,你可以指定当目标文件已经存在时是否覆盖它,或者是否在移动文件时保留其源文件的权限和属性。相比之下,java.io.File.renameTo() 方法没有这些选项。
java.nio.file.Files.move() 方法支持在不同文件系统之间移动文件。例如,你可以使用它在 Windows 和 Linux 之间移动文件。相比之下,java.io.File.renameTo() 方法只能在同一文件系统内移动文件。
总的来说,如果你需要在不同文件系统之间移动文件,或者希望在移动文件时使用更多的选项,则应使用 java.nio.file.Files.move() 方法。如果你只需要在同一文件系统内移动文件,并且希望使用简单的方法,则可以使用 java.io.File.renameTo() 方法。
但是,请注意,java.io.File.renameTo() 方法在某些情况下可能不起作用。例如,如果你在 Windows 上尝试使用该方法移动文件时,它可能会失败,因为 Windows 在打开文件时会占用该文件,并阻止对其进行更改。在这种情况下,使用 java.nio.file.Files.move() 方法可能会更加可靠。

4. java 文件夹拷贝

@RequiresApi(api = Build.VERSION_CODES.O)
    public static void copyFolder(String source, String target) {
        final Path sourcePath = Paths.get(source);
        final Path targetPath = Paths.get(target);
        try {
            Files.walkFileTree(sourcePath, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    Path targetFile = targetPath.resolve(sourcePath.relativize(file));
                    Files.copy(file, targetFile, StandardCopyOption.REPLACE_EXISTING);
                    return FileVisitResult.CONTINUE;
                }

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                    Path targetDir = targetPath.resolve(sourcePath.relativize(dir));
                    Files.createDirectory(targetDir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            Log.d(TAG, "copyFolder:IOException: " + e.getMessage());
            e.printStackTrace();
        }

    }

5. 压缩多个文件到一个zip包中

/**
     * 压缩多个文件到一个zip包
     *
     * @param filePaths     需要压缩的文件列表
     * @param targetZipPath 目标zip包路径
     * @throws IOException
     */
    public static void zipFiles(List<String> filePaths, String targetZipPath) throws IOException {
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(targetZipPath))) {
            for (String filePath : filePaths) {
                File file = new File(filePath);
                try (FileInputStream fileInputStream = new FileInputStream(file)) {
                    ZipEntry zipEntry = new ZipEntry(file.getName());
                    zipOutputStream.putNextEntry(zipEntry);
                    byte[] bytes = new byte[1024];
                    int length;
                    while ((length = fileInputStream.read(bytes)) >= 0) {
                        zipOutputStream.write(bytes, 0, length);
                    }
                }
            }
        }
    }

6. 总结:

android 单一平台,直接使用 renameTo 就可以;
如果需要跨平台拷贝,或者自定义是否覆盖目标文件,是否保留源文件,那就使用 Files.move;
上面两种方式都比通过文件流拷贝效率要高。