Java NIO中FileChannel复制文件首选transferTo(),需手动分段循环处理超2GB文件,注意跨平台零拷贝限制及资源关闭;日常开发推荐更健壮的Files.copy()。

Java NIO 中用 FileChannel 复制文件,核心是利用 transferTo() 或 transferFrom() 方法实现高效、接近零拷贝的传输,尤其适合大文件场景。它比传统流式复制快得多,但需注意平台限制和边界细节。
用 transferTo 实现高效文件复制
这是最常用也最推荐的方式,底层可借助操作系统零拷贝能力(如 Linux 的 copy_file_range 或 sendfile),避免数据在用户态和内核态之间来回拷贝。
- 必须通过
FileInputStream.getChannel()或RandomAccessFile.getChannel()获取源通道,FileOutputStream.getChannel()获取目标通道 - 单次
transferTo(position, count, targetChannel)最多传输Integer.MAX_VALUE字节(约 2GB),超长文件需循环调用 - 不要直接依赖
channel.size()一次性传完——文件可能被并发修改,应边传边更新 position 和剩余量
正确处理大文件的循环复制逻辑
绕过 2GB 限制的关键是手动分段,每次控制传输长度,并检查实际传输字节数:
- 初始化
long pos = 0和long remaining = sourceChannel.size() - 循环中计算
toTransfer = Math.min(remaining, Integer.MAX_VALUE) - 调用
sourceChannel.transferTo(pos, toTransfer, targetChannel),返回值可能小于toTransfer(如磁盘满、中断、跨文件系统) - 更新
pos += transferred和remaining -= transferred,直到remaining == 0
注意事项与常见陷阱
看似简单的方法,在不同环境表现差异较大,需提前规避风险:
立即学习“Java免费学习笔记(深入)”;
- Linux 上:只有源和目标都是普通文件且文件系统支持(如 ext4/xfs + 内核 ≥5.3)时才真正走零拷贝;NFS、overlayfs、/proc 文件等会静默降级为用户态拷贝
- Windows 上:
transferTo对两个本地文件始终不走零拷贝,等价于先读入 ByteBuffer 再写出,性能优势有限 - 务必关闭所有资源:
FileInputStream、FileOutputStream及其FileChannel,建议用 try-with-resources - 目标文件不存在时,
FileOutputStream默认会创建;若需保留权限或时间戳,需额外调用Files.setAttribute()或改用Files.copy()
对比 Files.copy() 的适用选择
Files.copy(source, target, ...) 底层其实也会优先尝试 transferTo,失败后自动回退到带缓冲区的流式复制,更健壮、代码更简洁:
- 日常开发推荐用
Files.copy():支持REPLACE_EXISTING、COPY_ATTRIBUTES,异常语义清晰,无需手动管理 channel 和循环 - 极致性能且明确可控场景(如服务端批量归档 >10GB 文件)再上手写
FileChannel循环逻辑 - 若需并发复制多个文件,
FileChannel本身不支持非阻塞,但可配合线程池 + 多个独立 channel 实现并行


















