从ZIP文件解压并抽取数据
java.util.zip
包提供了数据压缩和解压缩的类。解压ZIP文件实质是从输入流中读出数据。java.util.zip
包提供了读取ZIP文件的ZipInputStream
类。可以像任何其他输入流那样创建 ZipInputStream
。例如,下列代码可用于创建输入流,以从ZIP文件格式中读出数据:
FileInputStream fis = new FileInputStream("figs.zip"); ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis));
一旦打开ZIP输入流,就可以使用
方法读取zip条目,该方法返回getNextEntry
ZipEntry
对象。如果到达文件末尾, getNextEntry
就会返回零值:
ZipEntry entry; while((entry = zin.getNextEntry()) != null) { // extract data // open output streams }
现在创建解压缩输出流,如下:
int BUFFER = 2048; FileOutputStream fos = new FileOutputStream(entry.getName()); BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);
注意: 在此节代码中,我们用BufferedOutputStream
代替了ZIPOutputStream
。ZIPOutputStream
和GZIPOutputStream
使用大小为 512 的内部缓冲。BufferedOutputStream
的使用仅在缓冲的大小远远超过 512 时 (本例的设置为 2048)要予以调整。不过,在ZIPOutputStream
的情况下,当GZIPOutputStream
不许设置缓冲区大小时,可以将内部缓冲区大小指定为设计参数。
在本节代码中,文件输出流是使用条目的名称创建的,该名称可以使用 entry.getName
方法进行检索。然后,源压缩数据会读写到解压流:
while ((count = zin.read(data, 0, BUFFER)) != -1) { //System.out.write(x); dest.write(data, 0, count); }
最后,结束输入与输出流∶
dest.flush(); dest.close(); zin.close();
代码样本 1 中的源程序介绍了如何从 ZIP 文件中解压和提取文件。要测试 此样本,可对类进行编译,并通过传递 ZIP 格式的已压缩文件来进行运行:
prompt> java UnZip somefile.zip
注意,somefile.zip
可以是任何ZIP兼容工具,如WinZip,创建的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
类是连续地读取ZIP文件的。不过,类ZipFile
使用内部随机存取文件读取ZIP文件的内容,所以不必连续地读取ZIP文件的条目。
注意:ZIPInputStream
和ZipFile
间的另一个基本区别是在高速缓存方面。在结合使用ZipInputStream
和FileInputStream
读取文件时,Zip条目没有进行高速缓存。不过,如果文件是用ZipFile(fileName)
打开的,然后在内部进行高速缓存,那么,在ZipFile(fileName)
被再次调用时,该文件仅仅被打开一次。高速缓存值应用在第二次打开时。如果使用的是 UNIX,则值得注意的是,使用ZipFile
打开的全部ZIP文件都是内存映射的,因此ZipFile
的性能优越于ZipInputStream
。不过,如果相同ZIP文件的内容在程序执行期间要经常地进行更改和重新加载,那么使用ZipInputStream
效果更好。
下面是 ZIP 文件使用ZipFile
类进行解压的方式:
- 创建
ZipFile
对象,方式是将待读取的ZIP文件指定为字符串文件名或 File 对象。
ZipFile zipfile = new ZipFile("figs.zip");
- 使用条目方法,返回
Enumeration
对象,以循环通过文件全部的ZipEntry
对象:while(e.hasMoreElements()) { entry = (ZipEntry) e.nextElement(); // read contents and save them }
- 通过将
ZipEntry
传递到getInputStream
的方式,读取 ZIP 文件内部具体的ZipEntry
,它将返回可以读取条目内容的InputStream
对象:is = new BufferedInputStream(zipfile.getInputStream(entry));
- 检索条目的文件名并创建输出流,以便保存:
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();
完整的源程序如代码样本2所示。为测试该类,请再次进行编译和运行,方式是传递 ZIP 格式参数的文件。
prompt> java UnZip2 somefile.zip
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 文件中的数据
ZipOutputStream
可用于将数据压缩到ZIP文件。 ZipOutputStream
以ZIP格式将数据写入输出流。创建ZIP文件的步骤很多。
- 第一步是创建
ZipOutputStream
对象,将向它传递希望写入文件的输出流。创建名为“myfigs.zip”的ZIP文件的方式是:FileOutputStream dest = new FileOutputStream("myfigs.zip"); ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
- 一旦创建了目标压缩输出流,下一步就是打开源数据文件。本例中的源数据文件在当前目录中。列表命令用来获取当前目录文件的列表:
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 }
注意: 本代码样本能够压缩当前目录中的所有文件。它不处理子目录。作为训练,您可以修改代码样本 3,以处理子目录。
- 为被读取的每个文件创建压缩条目:
ZipEntry entry = new ZipEntry(files[i]))
- 在向ZIP输出流写入数据之前,您必须首先使用
out.putNextEntry(entry);
方法安置压缩条目对象∶out.putNextEntry(entry);
- 向ZIP 文件写入数据∶
int count; while((count = origin.read(data, 0, BUFFER)) != -1) { out.write(data, 0, count); }
- 最后,结束输入与输出流∶
origin.close(); out.close();
完整的源程序如代码样本 3 所示。
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
可用于设置存储的方法。例如,将方法设置为 DEFLATED (压缩),请使用:
out.setMethod(ZipOutputStream.DEFLATED)
;将它设置为 STORED (非压缩),请使用:out.setMethod
( ZipOutputStream.STORED)
。