Java中用FileChannel复制大文件核心是零拷贝机制,通过transferTo/transferFrom调用系统sendfile/CopyFile实现内核空间直传,需分段传输、正确打开关闭channel并确保目标目录存在。

Java 中用 FileChannel 复制大文件,核心是利用 零拷贝(zero-copy) 机制,避免 JVM 堆内存参与数据搬运,直接由操作系统在内核空间完成传输,比传统 InputStream/OutputStream 快得多,尤其适合几百 MB 到数 GB 的文件。
用 transferTo() 或 transferFrom() 实现高效复制
这两个方法底层调用系统级的 sendfile(Linux)或 CopyFile(Windows),不经过用户态缓冲区。注意:不是所有平台都完全支持跨文件系统或超大偏移量,但日常本地磁盘复制基本无问题。
-
transferTo(position, count, targetChannel):从当前 channel 读取最多count字节,写入目标 channel 的当前位置 -
transferFrom(sourceChannel, position, count):从源 channel 读取最多count字节,写入当前 channel 的指定位置 - 实际使用时建议分段传输(如每次最多 2GB),避免某些 JDK 版本对单次
count的限制(例如 JDK 8 在 64 位系统上最大支持约Integer.MAX_VALUE字节)
正确打开和关闭 FileChannel(推荐 try-with-resources)
必须确保源和目标 channel 都基于 FileInputStream 和 FileOutputStream(不能是 RandomAccessFile 的只读/只写模式混用),且要显式处理 position 和 size。
- 源 channel 用
FileInputStream.getChannel(),目标用FileOutputStream.getChannel() - 目标文件要提前创建好(
FileOutputStream构造时设append=false,默认覆盖) - 务必用 try-with-resources 自动关闭,否则 channel 泄漏会导致文件句柄耗尽
完整可运行示例(含分段处理)
以下代码安全处理任意大小文件,兼容 JDK 7+:
public static void copyWithChannel(Path source, Path target) throws IOException {
try (FileChannel in = FileChannel.open(source, StandardOpenOption.READ);
FileChannel out = FileChannel.open(target, StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
<pre class="brush:php;toolbar:false;"> long size = in.size();
long offset = 0;
long maxCount = 2L * 1024 * 1024 * 1024; // 2GB 每次
while (offset <p>}</p>说明:
– in.size() 获取总长度,避免循环中重复调用
– transferTo 返回实际传输字节数(可能小于请求值,需累加)
– 不依赖 position(),手动控制偏移更可靠
注意事项与常见坑
这些细节不处理好,容易导致复制不全、卡死或抛异常:
- 目标目录必须已存在,
FileChannel.open()不会自动创建父目录 - Windows 下若目标文件正被其他程序打开(如 Excel、记事本),会抛
AccessDeniedException - 不要在
transferTo后立刻读取目标文件——部分系统延迟刷盘,如需立即可见,调用out.force(false) - Android 上
transferTo可能受限(尤其旧版本),建议降级为ByteBuffer+read/write方案
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











