将数据压缩归档入一ZIP文件

类ZipOutputStream能够用来将数据压缩成一个ZIP文件。ZipOutputStream将数据写入ZIP格式的输出流。下面的步骤与创建一个ZIP文件相关。

1、第一步是创建一个ZipOutputStream对象,我们将要写入输出流的文件作为参数传给它。下面的代码演示了如何创建一个名为"myfigs.zip"的ZIP文件。

FileOutputStream dest = new
FileOutputStream("myfigs.zip");
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));

2、一但目标输出流创建后,下一步就是打开数据源文件。在这个例子中,源数据文件是指那些当前目录下的文件。命令list用来得到当前目录下文件列表:

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来处理子目录。

3、 为读出的数据创建一个ZIP条目列表:

ZipEntry entry = new ZipEntry(files[i]))

4、 在你将数据写入ZIP输出流之前,你必须使用putNextEntry方法将ZIP条目列表写入输出流:

out.putNextEntry(entry);

5、 将数据写入ZIP文件:

int count;
while((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}

6、 最后关闭所有的输入输出流:

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();
}}
}

注意: 条目列表可以以两种方式加入ZIP文件中,一种是压缩方式(DEFLATED),另一种是不压缩方式(STORED),系统默认的存储方式为压缩方式(DEFLATED)。SetMethod方法可以用来设置它的存储方式。例如,设置存储方式为DEFLATED(压缩)应该这样做: out.setMethod(ZipOutputStream.DEFLATED) 设置存储方式为(不压缩)应该这样做: out.setMethod(ZipOutputStream.STORED)。

ZIP文件属性

类ZipEntry描述了存储在ZIP文件中的压缩文件。类中包含有多种方法可以用来设置和获得ZIP条目的信息。类ZipEntry是被ZipFile和ZipInputStream使用来读取ZIP文件,ZipOutputStream来写入ZIP文件的。ZipEntry中最有用的一些方法显示在下面的表格2中,并且有相应的描述。

表格 2: 类ZipEntry中一些有用的方法

方法签名 描述

public String getComment() 返回条目的注释, 没有返回null

public long getCompressedSize() 返回条目压缩后的大小, 未知返回-1

public int getMethod() 返回条目的压缩方式,没有指定返回 -1

public String getName() 返回条目的名称

public long getSize() 返回未被压缩的条目的大小,未知返回-1

public long getTime() 返回条目的修改时间, 没有指定返回-1

public void setComment(String c) 设置条目的注释

public void setMethod(int method) 设置条目的压缩方式

public void setSize(long size) 设置没有压缩的条目的大小

public void setTime(long time) 设置条目的修改时间

求和校验

java.util.zip包中另外一些比较重要的类是Adler32和CRC32,它们实现了java.util.zip.Checksum接口,并估算了压缩数据的校验和(checksum)。众所周知,在运算速度方面,Adler32算法比CRC32算法要有一定的优势;但在数据可信度方面,CRC32算法则要更胜一筹。正所谓,"鱼与熊掌,不可兼得。",大家只好在不同的场合下,加以取舍了。GetValue方法可以用来获得当前的checksum值,reset方法能够重新设置checksum为其缺省的值。

求和校验一般用来校验文件和信息是否正确的传送。举个例子,假设你想创建一个ZIP文件,然后将其传送到远程计算机上。当到达远程计算机后,你就可以使用checksum检验在传输过程中文件是否发生错误。为了演示如何创建checksums,我们修改了例程1和例程3,在例程4和例程5中使用了两个新类,一个是CheckedInputStream,另一个是CheckedOutputStream。(大家注意:这两段代码在压缩与解压缩过程中,使用了同一种算法,求数据的checksum值。)

例程4源代码:

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");
CheckedOutputStream checksum = new
CheckedOutputStream(dest, new Adler32());
ZipOutputStream out = new
ZipOutputStream(new
BufferedOutputStream(checksum));
//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();
System.out.println("checksum:
"+checksum.getChecksum().getValue());
} catch(Exception e) {
e.printStackTrace();
}}
}

例程5源代码:

UnZip.java
import java.io.*;
import java.util.zip.*;
public class UnZip {
public static void main (String argv[]) {
try {
final int BUFFER = 2048;
BufferedOutputStream dest = null;
FileInputStream fis = new
FileInputStream(argv[0]);
CheckedInputStream checksum = new
CheckedInputStream(fis, new Adler32());
ZipInputStream zis = new
ZipInputStream(new
BufferedInputStream(checksum));
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();
System.out.println("Checksum:
"+checksum.getChecksum().getValue());
} catch(Exception e) {
e.printStackTrace();
}
}}

测试例程4和5,编译类文件并运行类Zip来创建一个压缩档案(程序会计算出checksum值并显示在屏幕上),然后运行UnZip类来解压缩这个档案(屏幕上同样会打印出一个checksum值)。两个值必须完全相同,否则说明出错了。Checksums在数据校验方面非常有用。例如,你可以创建一个ZIP文件,然后连同checksum值一同传递给你的朋友。你的朋友解压缩文件后,将生成的checksum值与你提供的作一比较,如果相同则说明在传递过程中没有发生错误。