二进制文件拷贝应使用 fileinputstream 和 fileoutputstream 原样读写字节,关键在于不丢字节、不乱序、及时关闭流;推荐缓冲区大小为 8kb~1mb,必须用 read 返回值控制 write 范围,避免 available() 和 reader/writer,并用 try-with-resources 确保资源释放。

用 FileInputStream 和 FileOutputStream 拷贝二进制文件,核心就是“原样读、原样写”——不解析内容,只搬运字节。关键不在速度多快,而在不丢字节、不乱顺序、不漏关闭。只要缓冲区设得合理、流关得及时,对普通硬盘或SSD来说,已经足够高效。
选对缓冲区大小,别小气也别贪大
一次读太多(比如 100MB),内存吃紧;一次读太少(比如 1 字节),磁盘反复寻道,拖慢整体速度。实践中,8KB~1MB 的 byte 数组最常用,兼顾内存与 I/O 效率。
- 推荐用
new byte[8192](8KB)或new byte[1024 * 1024](1MB) - 每次
fis.read(b)返回实际读到的字节数len,必须用fos.write(b, 0, len)写入,不能直接write(b)—— 否则末尾会补零,损坏图片、视频等文件 - 避免用
fis.available()当缓冲区大小(它只反映当前可读字节数,不是文件总长,且对某些流不可靠)
必须显式关闭流,否则资源卡死
流对象底层绑定了操作系统句柄(file descriptor)。不关,轻则下次拷贝失败,重则耗尽系统资源。手动关闭要分两步处理:
- 先关
FileOutputStream(确保数据真正落盘),再关FileInputStream - 每个
close()都要单独 try-catch,防止前一个异常导致后一个没机会关 - 更稳妥的做法是用 try-with-resources(Java 7+),自动在作用域结束时关闭:
FileOutputStream fos = new FileOutputStream(dst)) {
// 读写逻辑
}
目标路径和覆盖逻辑要提前想清楚
FileOutputStream 默认是覆盖模式。如果目标文件已存在,旧内容会被清空重写。这不是 bug,是设计行为。
- 想追加写入(如拼接日志),构造时传
true:new FileOutputStream(dst, true) - 想确保目标目录存在,先调用
dst.getParentFile().mkdirs() - 想避免意外覆盖,可先检查目标文件是否存在:
if (!dst.exists()) { ... }
二进制文件拷贝无需区分类型
JPG、MP4、ZIP、EXE……只要是二进制文件,就一律按字节流处理。不需要解码、不关心编码、不碰字符集。所谓“万能拷贝”,指的就是这个:同一套代码,换路径就能复制任意二进制文件。
- 千万别用
Reader/Writer处理图片或压缩包——它们会按字符编码解析字节,必然损坏数据 - 也不必为不同扩展名写不同逻辑,
FileInputStream → byte[] → FileOutputStream这条链路通用











