java文件拷貝方式

 

java文件拷貝方式

java.io提供了FileInputStream、FileOutputStream文件拷貝方式


public static void copyFileByStream(File source, File dest) throws
        IOException {
    try (InputStream is = new FileInputStream(source);
         OutputStream os = new FileOutputStream(dest);){
        byte[] buffer = new byte[1024];
        int length;
        while ((length = is.read(buffer)) > 0) {
            os.write(buffer, 0, length);
        }
    }
 }

java.nio提供transferTo,transferFrom可進行文件拷貝方式


public static void copyFileByChannel(File source, File dest) throws
        IOException {
    try (FileChannel sourceChannel = new FileInputStream(source)
            .getChannel();
         FileChannel targetChannel = new FileOutputStream(dest).getChannel
                 ();){
        for (long count = sourceChannel.size() ;count>0 ;) {
            long transferred = sourceChannel.transferTo(
                    sourceChannel.position(), count, targetChannel);            sourceChannel.position(sourceChannel.position() + transferred);
            count -= transferred;
        }
    }
 }

nio提供的transferTo,transferFrom的文件拷貝方式更爲高效,其採用zero-copy技術

拷貝實現機制

首先需要理解用戶態空間(user-space)和內核態空間(kernal-space),操作系統內核、硬件驅動等運行在內核態空間,具有相對高的特權;而用戶態空間,則是給普通應用和服務使用。

沒有使用zero-copy技術的文件拷貝圖:

其流程:1.將文件從磁盤複製到kernel,2.kernel-space拷貝到user-space,3,用戶user-space複製到kernel,4.kernel複製到磁盤

而使用zero-copy技術:

其流程:1.將文件從磁盤複製到kernel,2,senderkernel->accepter kernel,3.kernel複製到磁盤

減少了Io傳輸,減少了CPU切換,從而加快文件拷貝

Java IO/NIO源碼分析

Java 標準庫也提供了文件拷貝方法(java.nio.file.Files.copy),copy 不僅僅是支持文件之間操作

private static long copy(InputStream source, OutputStream sink)
      throws IOException

public static Path copy(Path source, Path target, CopyOption... options)
  throws IOException

public static long copy(InputStream in, Path target, CopyOption... options)
  throws IOException

public static long copy(Path source, OutputStream out) 
throws IOException

copy 方法其實不是利用 transferTo,而是本地技術實現的用戶態拷貝。

如何提高類似拷貝等 IO 操作的性能,有一些寬泛的原則:

  • 使用緩存等機制,合理減少 IO 次數
  • 使用 transferTo 等機制,減少上下文切換和額外io
  • 儘量減少不必要的轉換過程,比如編解碼;對象序列化和反序列化
public static Path copy(Path source, Path target, CopyOption... options)
    throws IOException
 {
    FileSystemProvider provider = provider(source);
    if (provider(target) == provider) {
        // same provider
        provider.copy(source, target, options);// 這是本文分析的路徑
    } else {
        // different providers
        CopyMoveHelper.copyToForeignTarget(source, target, options);
    }
    return target;
}

NIO源碼

Buffer 是 NIO 操作數據的基本工具,Java 爲每種原始數據類型都提供了相應的 Buffer 實現(布爾除外)。

Buffer 有幾個基本屬性:

  • capcity,它反映這個 Buffer 到底有多大,也就是數組的長度
  • position,要操作的數據起始位置。
  • limit,相當於操作的限額。在讀取或者寫入時,limit 的意義不一樣,
    -mark,記錄上一次 postion 的位置,默認是 0,算是一個便利性的考慮,往往不是必須的。

Buffer 的基本操作:

  • 我們創建了一個 ByteBuffer,準備放入數據,capcity 當然就是緩衝區大小,而 position 是 0,limit 默認就是 capcity 的大小。
  • 當我們寫入幾個字節的數據時,position 就會跟着水漲船高,但是它不可能超過 limit 的大小。
  • 如果我們想把前面寫入的數據讀出來,需要調用 flip 方法,將 position 設置爲 0,limit 設置爲以前的 position 那裏。
  • 如果還想從頭再讀一遍,可以調用 rewind,讓 limit 不變,position 再次設置爲 0。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章