從ZIP文件解壓并抽取數(shù)據(jù)
java.util.zip 包提供了數(shù)據(jù)壓縮和解壓縮的類。解壓ZIP文件實(shí)質(zhì)是從輸入流中讀出數(shù)據(jù)。java.util.zip 包提供了讀取ZIP文件的ZipInputStream 類??梢韵袢魏纹渌斎肓髂菢觿?chuàng)建 ZipInputStream 。例如,下列代碼可用于創(chuàng)建輸入流,以從ZIP文件格式中讀出數(shù)據(jù):
FileInputStream fis = new FileInputStream("figs.zip"); ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis));
一旦打開ZIP輸入流,就可以使用getNextEntry 方法讀取zip條目,該方法返回ZipEntry 對象。如果到達(dá)文件末尾, getNextEntry 就會(huì)返回零值:
ZipEntry entry; while((entry = zin.getNextEntry()) != null) { // extract data // open output streams }
現(xiàn)在創(chuàng)建解壓縮輸出流,如下:
int BUFFER = 2048; FileOutputStream fos = new FileOutputStream(entry.getName()); BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
注意: 在此節(jié)代碼中,我們用BufferedOutputStream 代替了ZIPOutputStream 。 ZIPOutputStream 和 GZIPOutputStream 使用大小為 512 的內(nèi)部緩沖。 BufferedOutputStream 的使用僅在緩沖的大小遠(yuǎn)遠(yuǎn)超過 512 時(shí) (本例的設(shè)置為 2048)要予以調(diào)整。不過,在ZIPOutputStream 的情況下,當(dāng)GZIPOutputStream 不許設(shè)置緩沖區(qū)大小時(shí),可以將內(nèi)部緩沖區(qū)大小指定為設(shè)計(jì)參數(shù)。
在本節(jié)代碼中,文件輸出流是使用條目的名稱創(chuàng)建的,該名稱可以使用 entry.getName 方法進(jìn)行檢索。然后,源壓縮數(shù)據(jù)會(huì)讀寫到解壓流:
while ((count = zin.read(data, 0, BUFFER)) != -1) { //System.out.write(x); dest.write(data, 0, count); }
最后,結(jié)束輸入與輸出流∶
dest.flush(); dest.close(); zin.close();
代碼樣本 1 中的源程序介紹了如何從 ZIP 文件中解壓和提取文件。要測試 此樣本,可對類進(jìn)行編譯,并通過傳遞 ZIP 格式的已壓縮文件來進(jìn)行運(yùn)行:
prompt> java UnZip somefile.zip
注意,somefile.zip 可以是任何ZIP兼容工具,如WinZip,創(chuàng)建的ZIP文件。
代碼樣本 1: UnZip.java
import java.io.*; import java.util.zip.*; public class UnZip { final int BUFFER = 2048; public static void main (String argv[]) { try { BufferedOutputStream dest = null; FileInputStream fis = new FileInputStream(argv[0]); ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis)); ZipEntry entry; while((entry = zis.getNextEntry()) != null) { System.out.println("Extracting: " +entry); int count; byte data[] = new byte[BUFFER]; // write the files to the disk FileOutputStream fos = new FileOutputStream(entry.getName()); dest = new BufferedOutputStream(fos, BUFFER); while ((count = zis.read(data, 0, BUFFER)) != -1) { dest.write(data, 0, count); } dest.flush(); dest.close(); } zis.close(); } catch(Exception e) { e.printStackTrace(); } } }
重要的是,要注意ZipInputStream 類是連續(xù)地讀取ZIP文件的。不過,類ZipFile 使用內(nèi)部隨機(jī)存取文件讀取ZIP文件的內(nèi)容,所以不必連續(xù)地讀取ZIP文件的條目。
注意: ZIPInputStream 和ZipFile 間的另一個(gè)基本區(qū)別是在高速緩存方面。在結(jié)合使用ZipInputStream 和 FileInputStream 讀取文件時(shí),Zip條目沒有進(jìn)行高速緩存。不過,如果文件是用ZipFile(fileName) 打開的,然后在內(nèi)部進(jìn)行高速緩存,那么,在ZipFile(fileName) 被再次調(diào)用時(shí),該文件僅僅被打開一次。高速緩存值應(yīng)用在第二次打開時(shí)。如果使用的是 UNIX,則值得注意的是,使用ZipFile 打開的全部ZIP文件都是內(nèi)存映射的,因此ZipFile 的性能優(yōu)越于ZipInputStream 。不過,如果相同ZIP文件的內(nèi)容在程序執(zhí)行期間要經(jīng)常地進(jìn)行更改和重新加載,那么使用ZipInputStream 效果更好。
下面是 ZIP 文件使用ZipFile 類進(jìn)行解壓的方式:
- 創(chuàng)建
ZipFile 對象,方式是將待讀取的ZIP文件指定為字符串文件名或 File 對象。
ZipFile zipfile = new ZipFile("figs.zip");
- 使用條目方法,返回
Enumeration 對象,以循環(huán)通過文件全部的ZipEntry 對象:
while(e.hasMoreElements()) { entry = (ZipEntry) e.nextElement(); // read contents and save them }
- 通過將
ZipEntry 傳遞到getInputStream 的方式,讀取 ZIP 文件內(nèi)部具體的ZipEntry ,它將返回可以讀取條目內(nèi)容的InputStream 對象:
is = new BufferedInputStream(zipfile.getInputStream(entry));
- 檢索條目的文件名并創(chuàng)建輸出流,以便保存:
byte data[] = new byte[BUFFER]; FileOutputStream fos = new FileOutputStream(entry.getName()); dest = new BufferedOutputStream(fos, BUFFER); while ((count = is.read(data, 0, BUFFER)) != -1) { dest.write(data, 0, count); }
- 最后關(guān)閉所有輸入與輸出流∶
dest.flush(); dest.close(); is.close();
完整的源程序如代碼樣本2所示。為測試該類,請?jiān)俅芜M(jìn)行編譯和運(yùn)行,方式是傳遞 ZIP 格式參數(shù)的文件。
prompt> java UnZip2 somefile.zip
代碼樣本2: UnZip2.java
import java.io.*; import java.util.*; import java.util.zip.*; public class UnZip2 { static final int BUFFER = 2048; public static void main (String argv[]) { try { BufferedOutputStream dest = null; BufferedInputStream is = null; ZipEntry entry; ZipFile zipfile = new ZipFile(argv[0]); Enumeration e = zipfile.entries(); while(e.hasMoreElements()) { entry = (ZipEntry) e.nextElement(); System.out.println("Extracting: " +entry); is = new BufferedInputStream (zipfile.getInputStream(entry)); int count; byte data[] = new byte[BUFFER]; FileOutputStream fos = new FileOutputStream(entry.getName()); dest = new BufferedOutputStream(fos, BUFFER); while ((count = is.read(data, 0, BUFFER)) != -1) { dest.write(data, 0, count); } dest.flush(); dest.close(); is.close(); } } catch(Exception e) { e.printStackTrace(); } } }
壓縮和歸檔 ZIP 文件中的數(shù)據(jù)
ZipOutputStream 可用于將數(shù)據(jù)壓縮到ZIP文件。 ZipOutputStream 以ZIP格式將數(shù)據(jù)寫入輸出流。創(chuàng)建ZIP文件的步驟很多。
- 第一步是創(chuàng)建
ZipOutputStream 對象,將向它傳遞希望寫入文件的輸出流。創(chuàng)建名為“myfigs.zip”的ZIP文件的方式是:
FileOutputStream dest = new FileOutputStream("myfigs.zip"); ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
- 一旦創(chuàng)建了目標(biāo)壓縮輸出流,下一步就是打開源數(shù)據(jù)文件。本例中的源數(shù)據(jù)文件在當(dāng)前目錄中。列表命令用來獲取當(dāng)前目錄文件的列表:
File f = new File("."); String files[] = f.list(); for (int i=0; i<files.length; i++) { System.out.println("Adding: "+files[i]); FileInputStream fi = new FileInputStream(files[i]); // create zip entry // add entries to ZIP file }
注意: 本代碼樣本能夠壓縮當(dāng)前目錄中的所有文件。它不處理子目錄。作為訓(xùn)練,您可以修改代碼樣本 3,以處理子目錄。
- 為被讀取的每個(gè)文件創(chuàng)建壓縮條目:
ZipEntry entry = new ZipEntry(files[i]))
- 在向ZIP輸出流寫入數(shù)據(jù)之前,您必須首先使用
out.putNextEntry(entry); 方法安置壓縮條目對象∶ out.putNextEntry(entry);
- 向ZIP 文件寫入數(shù)據(jù)∶
int count; while((count = origin.read(data, 0, BUFFER)) != -1) { out.write(data, 0, count); }
- 最后,結(jié)束輸入與輸出流∶
origin.close(); out.close();
完整的源程序如代碼樣本 3 所示。
代碼樣本3: Zip.java
import java.io.*; import java.util.zip.*; public class Zip { static final int BUFFER = 2048; public static void main (String argv[]) { try { BufferedInputStream origin = null; FileOutputStream dest = new FileOutputStream("c:\\zip\\myfigs.zip"); ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest)); //out.setMethod(ZipOutputStream.DEFLATED); byte data[] = new byte[BUFFER]; // get a list of files from current directory File f = new File("."); String files[] = f.list(); for (int i=0; i<files.length; i++) { System.out.println("Adding: "+files[i]); FileInputStream fi = new FileInputStream(files[i]); origin = new BufferedInputStream(fi, BUFFER); ZipEntry entry = new ZipEntry(files[i]); out.putNextEntry(entry); int count; while((count = origin.read(data, 0, BUFFER)) != -1) { out.write(data, 0, count); } origin.close(); } out.close(); } catch(Exception e) { e.printStackTrace(); } } }
注意:條目可以添加到壓縮(DEFLATED)或未壓縮(STORED)表格中的 ZIP 文件里。setMethod 可用于設(shè)置存儲的方法。例如,將方法設(shè)置為 DEFLATED (壓縮),請使用: out.setMethod(ZipOutputStream.DEFLATED) ;將它設(shè)置為 STORED (非壓縮),請使用:out.setMethod ( ZipOutputStream.STORED) 。 |