1. 程式人生 > 其它 >壓縮檔案,IO拷貝優化。

壓縮檔案,IO拷貝優化。

public class TestIO {
    public static void main(String[] args) {
        long beginTime = System.currentTimeMillis();
        String ZIP_FILE = "1023.zip";
        File zipFile = new File(ZIP_FILE);
        File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip");
        try (// 在這裡宣告資源,jdk1.7以後會自動關閉關閉資源
             ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
             InputStream inputStream = new FileInputStream(src);) {
             zipOut.putNextEntry(new ZipEntry("1023.zip"));
             int trans = 0;
             while ((trans = inputStream.read()) != -1) {
                zipOut.write(trans);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("檔案大小: " + src.length());
        System.out.println("耗時: " + (System.currentTimeMillis() - beginTime));
    }
}

使用位元組輸入輸出流進行I/O拷貝,傳輸單位是一個位元組,速度緩慢。

public class TestIO2 {
    public static void main(String[] args) {
        String ZIP_FILE = "1023.zip";
        File zipFile = new File(ZIP_FILE);
        File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip");
        long beginTime = System.currentTimeMillis();
        try (
             // jdk以後 這裡宣告的變數會自動關閉申請的資源
             ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
             BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(zipOut);
             BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(src));
            ) {
            zipOut.putNextEntry(new ZipEntry("1023.zip"));
            int trans = 0;
            while ((trans = bufferedInputStream.read()) != -1) {
               bufferedOutputStream.write(trans);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("檔案大小: " + src.length());
        System.out.println("耗時: " + (System.currentTimeMillis() - beginTime));
    }
}

使用位元組緩衝輸入輸出流進行I/O拷貝,傳輸單位是一個位元組,速度緩慢。例如我們現在有30000個位元組的資料,如果使用 FileInputStream那麼就需要呼叫30000次的本地方法來獲取這些資料,而如果使用緩衝區的話(這裡假設初始的緩衝區大小足夠放下30000位元組的資料)那麼只需要呼叫一次就行。

public class TestIO3 {
    public static void main(String[] args) {
        String ZIP_FILE = "1023.zip";
        File zipFile = new File(ZIP_FILE);
        File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip");
        long beginTime = System.currentTimeMillis();
        try (
                // jdk以後 這裡宣告的變數會自動關閉申請的資源
                ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
                WritableByteChannel writableByteChannel = Channels.newChannel(zipOut);
                FileChannel channel = new FileInputStream(src).getChannel();
            ) {
            zipOut.putNextEntry(new ZipEntry("1023.zip"));
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            while (channel.read(buffer) != -1) {
                buffer.flip();
                writableByteChannel.write(buffer);
                buffer.clear();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("檔案大小: " + src.length());
        System.out.println("耗時: " + (System.currentTimeMillis() - beginTime));
    }
}

NIO中新出了 ChannelByteBuffer。正是因為它們的結構更加符合作業系統執行I/O的方式,所以其速度相比較於傳統IO而言速度有了顯著的提高。Channel就像一個包含著煤礦的礦藏,而 ByteBuffer則是派送到礦藏的卡車。也就是說我們與資料的互動都是與 ByteBuffer的互動。

public class TestIO4 {
    public static void main(String[] args) {
        String ZIP_FILE = "1023.zip";
        File zipFile = new File(ZIP_FILE);
        File src = new File("F:\\person_code\\jvm_deep_base\\module04\\src\\main\\java\\jdk1.8.0_291.zip");
        long beginTime = System.currentTimeMillis();
        try (
                // jdk以後 這裡宣告的變數會自動關閉申請的資源
                ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile));
                WritableByteChannel writableByteChannel = Channels.newChannel(zipOut);
                FileChannel channel = new FileInputStream(src).getChannel();
            ) {
            zipOut.putNextEntry(new ZipEntry("1023.zip"));
            channel.transferTo(0, src.length(), writableByteChannel);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("檔案大小: " + src.length());
        System.out.println("耗時: " + (System.currentTimeMillis() - beginTime));
    }
}

使用 transferTo的效率比迴圈一個 Channel讀取出來然後再迴圈寫入另一個 Channel好。作業系統能夠直接傳輸位元組從檔案系統快取到目標的 Channel中,而不需要實際的 copy階段。簡單來說省去了從核心空間轉到使用者空間的一個過程。關於零拷貝可以參考這篇部落格。