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 上找到。