Java中高效复制大文件的关键是减少内存占用,避免频繁的I/O操作,并利用操作系统级别进行优化。使用NIO(New I/O)中的FileChannel配合transferTo()或transferFrom()最推荐的方法是触发零拷贝(zero-copy)大大提高性能的机制。
高效复制FileChanel
通过FileChannel的transferTo()该方法可以直接在两个通道之间传输数据,减少上下文切换和内存副本,而无需通过用户空间缓冲区。
- 适用于GB级以上大文件复制
- Sendfile系统调用底层可调用操作系统
- 代码简单,性能优异
示例代码:
import java.io.IOException; import java.nio.channels.FileChannel; import java.nio.file.Path; import java.nio.file.StandardOpenOption; <p>public class FileCopyUtil { public static void copyLargeFile(Path source, Path target) throws IOException { try (FileChannel in = FileChannel.open(source, StandardOpenOption.READ); FileChannel out = FileChannel.open(target, StandardOpenOption.WRITE, StandardOpenOption.CREATE)) { long position = 0; long count = in.size(); while (position < count) { // transferto尝试一次最多传输2GB long transferred = in.transferTo(position, count - position, out); if (transferred == 0) break; // 防止无限循环 position += transferred; } } } }使用Files.copy()(简单但需要注意场景)
Java 7+ 提供了Files.copy()方法,底层也会尝试使用FileChannel.transferTo(),在大多数情况下,它已经足够高效了。
适用于快速实现、脚本化任务或中小文件复制。
示例:
import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardCopyOption; <p>Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
注:该方法对大型文件仍然有效,但最好直接控制FileChannel灵活。 避免使用传统流式逐字节/小缓冲复制
在处理大文件时,应避免以下方法:
- 使用
FileInputStream.read()单字节读取 - 使用较小的缓冲区(例如1KB)
BufferedInputStream - 频繁的
flush()操作
如果必须使用流量,至少使用较大的缓冲区(如8MB):
try (InputStream in = Files.newInputStream(source); OutputStream out = Files.newOutputStream(target)) { byte[] buffer = new byte[8 * 1024 * 1024]; // 8MB buffer int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } }基本上就是这些。对于大文件,优先考虑FileChannel.transferTo,兼顾性能和可控性。