Java 中 ZipFile 和 ZipInputStream 的區別
1. 概述
有時我們必須壓縮文件,將多個文件打包到一個存檔中,以便於傳輸和節省空間。對於此用例, Zip 是一種廣泛使用的壓縮存檔檔案格式。
Java 提供了一組標準的類別(如ZipFile
和ZipInputStream
來存取 zip 檔案。在本教程中,我們將學習如何使用它們來讀取 zip 檔案。此外,我們還將探討它們的功能差異並評估它們的性能。
2. 建立 Zip 文件
在我們深入研究讀取 zip 檔案的程式碼之前,讓我們先回顧一下建立 zip 檔案的過程。
在下面的程式碼片段中,我們將有兩個變數。 data
儲存要壓縮的內容, file
代表我們的目標檔案:
String data = "..."; // a very long String
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
ZipOutputStream zos = new ZipOutputStream(bos)) {
ZipEntry zipEntry = new ZipEntry("zip-entry.txt");
zos.putNextEntry(zipEntry);
zos.write(data);
zos.closeEntry();
}
此程式碼片段將data
歸檔到名為zip-entry.txt
的 zip 條目,然後將該條目寫入目標file
。
3.透過ZipFile
讀取
首先,讓我們看看如何透過ZipFile
類別讀取 zip 檔案中的所有項目:
try (ZipFile zipFile = new ZipFile(compressedFile)) {
Enumeration<? extends ZipEntry> zipEntries = zipFile.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry zipEntry = zipEntries.nextElement();
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
// Read data from InputStream
}
}
}
我們建立ZipFile
的實例來讀取壓縮檔。 ZipFile.entries()
傳回 zip 檔案中的所有 zip 條目。然後我們可以從ZipEntry
中取得InputStream
來讀取它的內容。
除了entries(), ZipFile
還有一個方法getEntry(…),
它允許我們根據條目名稱隨機存取特定的ZipEntry
:
ZipEntry zipEntry = zipFile.getEntry("str-data-10.txt");
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
// Read data from InputStream
}
4.透過ZipInputStream
讀取
接下來,我們將介紹一個透過ZipInputStream
從 zip 檔案中讀取所有條目的典型範例:
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(compressedFile));
ZipInputStream zipInputStream = new ZipInputStream(bis)) {
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
// Read data from ZipInputStream
}
}
我們建立一個ZipInputStream
來包裝資料來源,在我們的例子中是compressedFile
。之後,我們透過getNextEntry()
迭代ZipInputStream
。
在循環中,我們透過從ZipIputStream
讀取資料來讀取每個ZipEntry
的資料。一旦我們完成了一個條目的讀取,那麼我們再次呼叫getNextEntry()
來表示我們將讀取下一個條目。
5. 功能差異
儘管這兩個類別都可以用於從 zip 檔案中讀取條目,但它們有兩個明顯的功能差異。
5.1.存取類型
它們之間的主要區別在於ZipFile
支援隨機訪問,而ZipInputStream
僅支援順序訪問。
在ZipFile
中,我們可以透過呼叫ZipFile.getEntry(…)
來提取特定條目。當我們只需要ZipFile
中的特定條目時,此特性特別有利。如果我們想要在ZipInputStream
中實現相同的效果,我們必須循環遍歷每個ZipEntry
直到在迭代過程中找到匹配項。
5.2.資料來源
ZipFile
要求資料來源是實體文件,而ZipInputStream
只需要InputStream
。可能存在這樣的情況:我們的資料不是文件。例如,我們的資料來自網路流。在這種情況下,我們必須將整個InputStream
轉換為文件,然後才能使用ZipFile
來處理。
6. 效能比較
我們已經了解了ZipFile
和ZipInputStream
之間的功能差異。現在,讓我們探討一下效能方面的進一步差異。
我們將使用 JMH(Java Microbenchmark Harness)來捕捉兩者之間的處理速度。 JMH 是一個旨在測量程式碼片段效能的框架。
在進行基準測試之前,我們必須在pom.xml
中包含以下 Maven 依賴項:
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.37</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.37</version>
</dependency>
最新版本的 JMH Core和Annotation可以在 Maven Central 中找到。
6.1.閱讀所有條目
在本實驗中,我們的目標是評估從 zip 檔案中讀取所有條目的效能。在我們的設定中,我們有一個包含 10 個條目的 zip 文件,每個條目包含 200KB 的資料。我們將分別透過ZipFile
和ZipInputStream
讀取它們:
班級 | 運轉時間(以毫秒為單位) |
ZipFile | 11.072 |
ZipInputStream | 11.642 |
從結果來看,我們看不出兩個類別之間有任何顯著的效能差異。就運行時間而言,差異在 10% 以內。在讀取 zip 檔案中的所有條目時,它們都表現出了相當的效率。
6.2.閱讀最後一個條目
接下來,我們將專門讀取同一 zip 檔案中的最後一個條目:
班級 | 運轉時間(以毫秒為單位) |
ZipFile | 1.016 |
ZipInputStream | 12.830 |
這次他們之間的差距是巨大的。與讀取所有條目相比, ZipFile
只需要 1/10 的時間來讀取 10 個條目中的單一條目,而ZipInputStream
花費的時間幾乎相同。
我們可以觀察到ZipInputStream
從結果中順序讀取條目。必須從 zip 檔案的開頭讀取輸入流,直到找到目標條目,而ZipFile
允許跳到目標條目,而無需讀取整個檔案。
結果顯示選擇ZipFile
而不是ZipInputStream
的重要性,特別是在處理大量條目中的少量條目時。
七、結論
在軟體開發中,通常使用 zip 來處理壓縮檔。 Java 提供了兩個不同的類, ZipFile
和ZipIputStream,
來讀取 zip 檔案。
在本文中,我們探討了它們的用法和功能差異。我們也評估了他們之間的表現。
它們之間的選擇取決於我們的要求。當我們處理大型 zip 檔案中有限數量的條目時,我們將選擇ZipFile
以確保最佳效能。相反,如果我們的資料來源不是文件,我們將選擇ZipInputStream
。
與往常一樣,我們範例的完整原始程式碼可以在 GitHub 上找到。