首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 字節流拷貝文件實戰:從基礎 API 到 NIO 零拷貝優化

     編輯:Java 相關知識

文件拷貝是 I/O 入門必修課。本文對比傳統字節流逐字節拷貝、緩衝流塊拷貝、NIO transferTo 零拷貝、Files.copy 高級 API 的性能與適用場景,附完整可運行 Demo。

方案對比

方案核心 API機制適合場景相對耗時(1GB)
字節流單字節FileInputStream.read() / FileOutputStream.write(int)用戶態逐字節拷貝,系統調用極多教學演示~120s
字節數組緩衝read(byte[]) / write(byte[], 0, len)用戶態 4KB/8KB 塊拷貝小文件、舊版本兼容~2.5s
BufferedInput/OutputStreamBufferedInputStream / BufferedOutputStream內部 8KB 緩衝,減少系統調用通用文本/二進制拷貝~2.2s
NIO Channel.transferToFileChannel.transferTo(position, count, target)內核態零拷貝(sendfile/mmap),無用戶態緩衝區大文件、高性能要求~0.8s
Files.copy (JDK 7+)Files.copy(src, dst, REPLACE_EXISTING)底層自動選擇最優策略(transferTo/直接緩衝區)現代項目首選,代碼最簡潔~0.8s

代碼示例

1. 傳統緩衝流(最通用)

public void copyWithBuffer(File src, File dst) throws IOException {
    try (FileInputStream in = new FileInputStream(src);
         FileOutputStream out = new FileOutputStream(dst);
         BufferedInputStream bin = new BufferedInputStream(in);
         BufferedOutputStream bout = new BufferedOutputStream(out)) {
        byte[] buf = new byte[8192];
        int len;
        while ((len = bin.read(buf)) != -1) {
            bout.write(buf, 0, len);
        }
    }
}

2. NIO 零拷貝(大文件首選)

public void copyWithTransferTo(File src, File dst) throws IOException {
    try (FileChannel in = FileChannel.open(src.toPath(), StandardOpenOption.READ);
         FileChannel out = FileChannel.open(dst.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
        long size = in.size();
        long transferred = 0;
        while (transferred < size) {
            transferred += in.transferTo(transferred, size - transferred, out);
        }
    }
}

3. 現代 API 一行搞定(JDK 7+)

Files.copy(src.toPath(), dst.toPath(), StandardCopyOption.REPLACE_EXISTING);

進階:拷貝進度監控

public void copyWithProgress(File src, File dst, Consumer<Double> progress) throws IOException {
    try (FileChannel in = FileChannel.open(src.toPath(), StandardOpenOption.READ);
         FileChannel out = FileChannel.open(dst.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
        long size = in.size();
        long copied = 0;
        long chunk = 1024 * 1024; // 1MB
        ByteBuffer buf = ByteBuffer.allocateDirect((int) chunk);
        while (copied < size) {
            long remain = size - copied;
            long toRead = Math.min(chunk, remain);
            buf.limit((int) toRead);
            int read = in.read(buf);
            if (read == -1) break;
            buf.flip();
            out.write(buf);
            buf.clear();
            copied += read;
            progress.accept((double) copied / size);
        }
    }
}

常見坑點

  • 目標文件已存在:Files.copy 默認拋異常,需加 REPLACE_EXISTING。
  • 跨文件系統/網絡掛載:transferTo 可能不支援,需降級緩衝拷貝。
  • 拷貝稀疏文件:transferTo 會實體化空洞,導致目標文件膨脹。
  • 權限保留:Files.copy 不複製屬性,需 COPY_ATTRIBUTES 或手動 Files.setAttribute。

總結建議

  • JDK 7+ 項目:**首選 Files.copy**,簡潔且底層自動優化。
  • 需進度/限速/自定義緩衝:**NIO Channel + 直接緩衝區**。
  • 舊版本維護:**BufferedInput/OutputStream + 8KB 字節數組**。
  • 嚴禁單字節 read()/write(int) 用於生產環境文件拷貝。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved