文件複製是 I/O 操作基礎場景。本文提供生產級代碼模板,涵蓋流管理、緩衝策略、異常安全、大文件支持及 NIO.2 現代化替代方案。
/**
* 複製單個文件(BIO 版)
* @param srcPath 源文件絕對路徑
* @param destPath 目標文件絕對路徑
* @throws IOException I/O 錯誤時拋出
*/
public void copyFile(String srcPath, String destPath) throws IOException {
File srcFile = new File(srcPath);
if (!srcFile.exists() || !srcFile.isFile()) {
throw new FileNotFoundException("源文件不存在或非文件: " + srcPath);
}
// 目標目錄不存在則創建
File destFile = new File(destPath);
File destDir = destFile.getParentFile();
if (destDir != null && !destDir.exists()) {
destDir.mkdirs();
}
// try-with-resources 自動關閉流(JDK 7+)
try (FileInputStream fis = new FileInputStream(srcFile);
FileOutputStream fos = new FileOutputStream(destFile)) {
byte[] buffer = new byte[8192]; // 8KB 緩衝區,平衡內存與系統調用開銷
int bytesRead;
long totalBytes = 0;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
// 可選:進度回調、大文件分段校驗
}
System.out.println("複製完成: " + srcPath + " -> " + destPath + " (" + totalBytes + " bytes)");
}
// 流自動關閉,即使發生異常
}
| 要點 | 說明 |
|---|---|
| 緩衝區大小 | 4KB-64KB 為宜(8KB/16KB/32KB 常用)。過小增加系統調用,過大佔用內存且觸發 GC。磁盤頁面通常 4KB,8KB 是經驗甜點。 |
| try-with-resources | JDK 7+ 強制要求流實現 AutoCloseable,編譯器生成 finally 塊確保關閉,消除資源洩漏風險。 |
| 異常拋出而非吞噬 | 捕獲後打印堆棧並重新拋出,或聲明 throws,讓調用者決策處理(重試、告警、降級)。 |
| 目錄自動創建 | mkdirs() 避免因目標目錄不存在導致失敗。 |
| 進度/校驗擴展 | 大文件場景可集成進度監聽、MD5/CRC32 校驗、斷點續傳邏輯。 |
import java.nio.file.*;
import static java.nio.file.StandardCopyOption.*;
public void copyFileNIO(Path src, Path dest) throws IOException {
// 原子複製、保留屬性、覆蓋存在文件
Files.copy(src, dest, REPLACE_EXISTING, COPY_ATTRIBUTES, ATOMIC_MOVE);
// 或簡單版:Files.copy(src, dest, REPLACE_EXISTING);
}
優勢:
sendfile/CopyFileEx),性能極高// 可中斷、進度回調、限速複製
public void copyWithProgress(InputStream in, OutputStream out,
ProgressListener listener, long rateLimitBytesPerSec) throws IOException {
byte[] buffer = new byte[16384];
int bytesRead;
long total = 0;
long startTime = System.currentTimeMillis();
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
total += bytesRead;
// 限速邏輯
if (rateLimitBytesPerSec > 0) {
long elapsed = System.currentTimeMillis() - startTime;
long expectedTime = (total * 1000) / rateLimitBytesPerSec;
if (expectedTime > elapsed) {
Thread.sleep(expectedTime - elapsed);
}
}
// 進度回調
if (listener != null) {
listener.onProgress(total);
}
}
}
@FunctionalInterface
interface ProgressListener {
void onProgress(long bytesCopied);
}
| 方案 | 適用場景 | 性能 | 功能豐富度 |
|---|---|---|---|
Files.copy() (NIO.2) | 本地文件複製、備份、部署 | ⭐⭐⭐⭐⭐ (OS 零拷貝) | 原子性、屬性保留、稀疏文件 |
| BIO 緩衝流 | 舊版本 JDK、自定義邏輯(加密、壓縮、進度) | ⭐⭐⭐ | 完全可控、易擴展 |
FileChannel.transferTo/From | 大文件、網絡傳輸、零拷貝需求 | ⭐⭐⭐⭐⭐ | 需手動管理 Channel、位置 |
Apache Commons IO FileUtils.copyFile() | 快速開發、兼容性要求高 | ⭐⭐⭐ (內部用 BIO) | 目錄複製、過濾、清理等豐富工具 |
Files.copy()(JDK 7+)