湖北省建设厅官方网站资料网,seo优化工作有哪些,网站开发财务费用,北京住房建设网官网转载自 使用GZIP和Zip压缩Java数据流本文通过对数据压缩算法的简要介绍#xff0c;然后以详细的示例演示了利用java.util.zip包实现数据的压缩与解压#xff0c;并扩展到在网络传输方面如何应用java.util.zip包现数据压缩与解压综述
许多信息资料都或多或少的包含一些多余的…转载自 使用GZIP和Zip压缩Java数据流本文通过对数据压缩算法的简要介绍然后以详细的示例演示了利用java.util.zip包实现数据的压缩与解压并扩展到在网络传输方面如何应用java.util.zip包现数据压缩与解压综述
许多信息资料都或多或少的包含一些多余的数据。通常会导致在客户端与服务器之间应用程序与计算机之间极大的数据传输量。最常见的解决数据存储和信 息传送的方法是安装额外的存储设备和扩展现有的通讯能力。这样做是可以的但无疑会增加组织的运作成本。一种有效的解决数据存储与信息传输的方法是通过更 有效率的代码来存储数据。这篇文章简要的介绍了数据的压缩与解压缩并展示了用java.util.zip包来实现数据的压缩与解压缩是多么的方便与高 效。
当然用诸如WinZipgzip和Java压缩或jar之类的工具也可以实现数据的压缩与解压缩这些工具都是独立的应用程序。你也可以在 JAVA应用程序中调用这些工具但这并不是最直接的方法也不是有效的解决方法。尤其是你想更快速地实现数据的压缩与解压缩例如在传输数据到远程机器 之前。这篇文章包括以下内容
给出一个关于数据压缩的简单的介绍描述java.util.zip包示例如何使用该包实现数据的压缩与解压缩示例如何压缩串行化的对象并将其存储在磁碟上示例如何通过数据压缩来增强客户/服务应用程序的性能
数据压缩概述
文件中数据冗余的最简单的类型是字符的复制。让我们先来看下面一个字符串
JJJJJJAAAAVVVVAAAAAA 这个字符串可以用更简洁的方式来编码那就是通过替换每一个重复的字符串为单个的实例字符加上记录重复次数的数字来表示,上面的字符串可以被编码为下面的形式6J4A4V6A 在这里6J意味着6个字符J4A意味着4个字符A以此类推。这种字符串压缩方式称为行程长度编码方式简称RLE。再举一个例子考虑一下矩形图像的存储。一个单色位图可以被存储为下面这种形式如图1所示。另外一种方式是将图像存为一个图元文件
Rectangle 11, 3, 20, 5
上面的表示方法是讲矩形的起始坐标是113宽度是20高度是5。
上述的矩形图像可以使用RLE编码方式压缩通过对相同位记数表示如下
0, 400, 400,10 1,20 0,100,10 1,1 0,18 1,1 0,100,10 1,1 0,18 1,1 0,100,10 1,1 0,18 1,1 0,100,10 1,20 0,100,40
上面第一行是讲图像的第一行由40个0组成。第三行是讲图像的第三行是由10个0加上20个1再加上10个0组成其它行以此类推。
大家注意RLE方法需要将其表示的文件与编码文件分开。所以这种方法不能应用于所有的文件。其它的压缩技术包括变长编码也被称为哈夫曼编码还有其它的方法。要想了解更详细的信息请参考有关数据和图像压缩技术方面的图书一定会有收获的。
数据压缩有很多益处。不管怎么说最主要的好处就是减少存储方面的需求。同样的对于数据通信来讲压缩数据在媒体中的将导致信息传输数据的提升。 数据的压缩能够通过软件在现有的硬件设备上实现或者通过带有压缩技术的特殊的硬件设备来实现。图表2显示了基本的数据压缩结构图。ZIP VS GZIP
如果你是在Windows系统下工作你可能会对工具WinZip很熟悉是用来创建压缩档案和解开压缩档案的。而在UNIX平台上会有一些不同,命令tar用来创建一个档案文件并不压缩其它的程序gzip或compress用来创建一个压缩档案。
WinZip和PkZip之类的工具同时扮演着归档和压缩两个角色。他们将文件压缩并将其归档。另一方面gzip并不将文件归档。所以在UNIX平台上命令tar通常用来创建一个档案文件然后命令gzip来将档案文件压缩。
Java.util.zip包
Java提供了java.util.zip包用来兼容ZIP格式的数据压缩。它提供了一系列的类用来读取创建修改ZIP和GZIP格式的文件。 它还提供了工具类来计算任意输入流的数目这可以用来验证输入数据的有效性。该包提供了一个接口十四个类和两个异常处理类如表1所示。
条目类型描述Checksum接口被类Adler32和CRC32实现的接口Adler32类使用Alder32算法来计算Checksum数目CheckedInputStream类一个输入流保存着被读取数据的ChecksumCheckedOutputStream类一个输出流保存着被读取数据的ChecksumCRC32类使用CRC32算法来计算Checksum数目Deflater类使用ZLIB压缩类支持通常的压缩方式DeflaterOutputStream类一个输出过滤流用来压缩Deflater格式数据GZIPInputStream类一个输入过滤流读取GZIP格式压缩数据GZIPOutputStream类一个输出过滤流读取GZIP格式压缩数据Inflater类使用ZLIB压缩类支持通常的解压方式InlfaterInputStream类一个输入过滤流用来解压Inlfater格式的压缩数据ZipEntry类存储ZIP条目ZipFile类从ZIP文件中读取ZIP条目ZipInputStream类一个输入过滤流用来读取ZIP格式文件中的文件ZipOutputStream类一个输出过滤流用来向ZIP格式文件口写入文件DataFormatException异常类抛出一个数据格式错误ZipException异常类抛出一个ZIP文件
注意ZLIB压缩类最初是作为可移植的网络图像文件格式PNG标准的一部分开发的是不受专利保护的。
从ZIP文件中解压缩和提取数据
java.util.zip包提供了数据压缩与解压缩所需要的类。ZIP文件的解压缩实质上就是从输入流中读取数据。Java.util.zip包 提供了类ZipInputStream来读取ZIP文件。ZipInputStream流的创建与其它输入流的创建没什么两样。举个例子下面的代码段创 建了一个输入流来读取ZIP格式的文件
FileInputStream fis new FileInputStream(figs.zip);
ZipInputStream zin new ZipInputStream(new BufferedInputStream(fis));
ZIP输入流打开后你可以使用getNextEntry方法来读取ZIP文件中的条目数该方法返回一个ZipEntry对象。如果到达文件的尾部getNextEntry返回null
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字节缓冲。当缓冲区的大小大于512字节时使用 BufferedOutputStream才是正确的例子中设置为2048。ZIPOutputStream不允许你设置缓冲区的大 小GZIPOutputStream也是一样但创建 GZIPOutputStream 对象时可以通过构造函数的参数指定内置的缓冲尺寸。
这段代码中使用ZIP内含的条目名称创建一个文件输出流。可以使用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的源程序UnZip.java显示如何解压缩并从ZIP档案中将文件释放出来。测试这个例子编译这个类并运行它传给它一个ZIP格式的文件作为参数
prompt java UnZip somefile.zip
注意somefile.zip应该是一个ZIP压缩档案可以用任何一种ZIP压缩工具来创建例如WinZip。
UnZip.java
import java.io.*;
import java.util.zip.*;
public class UnZip {static 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 diskFileOutputStream 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文件序列简单地说就是读出这个ZIP文件压缩了多少文件而类ZipFile使用内嵌的随机文件访问机制读出其中的文件内容所以不必顺序的读出ZIP压缩文件序列。
注意ZIPInputStream和ZipFile之间另外一个基本的不同点在于高速缓冲的使用方面。当文件 使用ZipInputStream和FileInputStream流读出的时候ZIP条目不使用高速缓冲。然而如果使用ZipFile文件名来 打开文件它将使用内嵌的高速缓冲所以如果ZipFile文件名被重复调用的话文件只被打开一次。缓冲值在第二次打开进使用。如果你工作在 UNIX系统下这是什么作用都没有的因为使用ZipFile打开的所有ZIP文件都在内存中存在映射所以使用ZipFile的性能优于 ZipInputStream。然而如果同一ZIP文件的内容在程序执行期间经常改变或是重载的话使用ZipInputStream就成为你的首选 了。
下面显示了使用类ZipFile来解压一个ZIP文件的过程
通过指定一个被读取的ZIP文件或者是文件名或者是一个文件对象来创建一个ZipFile对象 ZipFile zipfile new ZipFile(figs.zip);使 用entries方法返回一个枚举对象循环获得文件的ZIP条目对象 while(e.hasMoreElements()) { entry (ZipEntry) e.nextElement(); // read contents and save them }ZIP 条目作为参数传递给getInputStream方法可以读取ZIP文件中指定条目的内容能过其返回的输入流InputStram对象可以方便的 读出ZIP条目的内容 is new BufferedInputStream(zipfile.getInputStream(entry));获 取ZIP条目的文件名创建输出流并保存: 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将数据压缩归档入一ZIP文件
类ZipOutputStream能够用来将数据压缩成一个ZIP文件。ZipOutputStream将数据写入ZIP格式的输出流。下面的步骤与创建一个ZIP文件相关。
1、第一步是创建一个ZipOutputStream对象我们将要写入输出流的文件作为参数传给它。下面的代码演示了如何创建一个名为myfigs.zip的ZIP文件。FileOutputStream dest newFileOutputStream(myfigs.zip);ZipOutputStream out new ZipOutputStream(new BufferedOutputStream(dest));
2、一但目标输出流创建后下一步就是打开数据源文件。在这个例子中源数据文件是指那些当前目录下的文件。命令list用来得到当前目录下文件列表
File f new File(.);
String files[] f.list();
for (int i0; 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所示。
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 directoryFile f new File(.);String files[] f.list();for (int i0; 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中并且有相应的描述。
方法签名描述public String getComment()返回条目的注释, 没有返回nullpublic long getCompressedSize()返回条目压缩后的大小, 未知返回-1public int getMethod()返回条目的压缩方式,没有指定返回 -1public String getName()返回条目的名称public long getSize()返回未被压缩的条目的大小未知返回-1public long getTime()返回条目的修改时间, 没有指定返回-1public 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值。
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 directoryFile f new File(.);String files[] f.list();for (int i0; 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();}}
}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 diskFileOutputStream 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值与你提供的作一比较如果相同则说 明在传递过程中没有发生错误。
压缩对象
我们已经看到如何将文件中的数据压缩并将其归档。但如果你想压缩的数据不在文件中时应该怎么办呢假设有这样一个例子你通过套接字 socket来传递一个大对象。为了提高应用程序的性能你可能在通过网络开始传递前将数据压缩然后在目的地将其解压缩。另外一个例子我们假设你 想将一个对象用压缩格式存储在磁碟上ZIP格式是基于记录方式的不适合这项工作。GZIP更适合用来实现这种对单一数据流的操作。现在我们来示例一 下如果在写入磁碟前将数据压缩并在读出时将数据解压缩。示例程序6是一个在单一JVMjava虚拟机实现了Serializable接口的简单 类我们想要串行化该类的实例。
Employee.java
import java.io.*;
public class Employee implements Serializable {String name;int age;int salary;public Employee(String name, int age, int salary) {this.name name;this.age age;this.salary salary;}public void print() {System.out.println(Record for: name);System.out.println(Name: name);System.out.println(Age: age);System.out.println(Salary: salary);}
}现在写另外一个类来创建两个从Employee类实例化而来的对象。示例程序7从Employee类创建了两个对象sarah和sam。然后将它们的状态以压缩的格式存储在一个文件中。
SaveEmployee.java
import java.io.*;
import java.util.zip.*;
public class SaveEmployee {public static void main(String argv[]) throws Exception {// create some objectsEmployee sarah new Employee(S. Jordan, 28, 56000);Employee sam new Employee(S. McDonald, 29, 58000);// serialize the objects sarah and samFileOutputStream fos new FileOutputStream(db);GZIPOutputStream gz new GZIPOutputStream(fos);ObjectOutputStream oos new ObjectOutputStream(gz);oos.writeObject(sarah);oos.writeObject(sam);oos.flush();oos.close();fos.close();}
}现在示例程序8中的ReadEmpolyee类是用来重新构建两个对象的状态。一但构建成功就调用print方法将其打印出来。
ReadEmployee.java
import java.io.*;
import java.util.zip.*;
public class ReadEmployee {public static void main(String argv[]) throws Exception{//deserialize objects sarah and samFileInputStream fis new FileInputStream(db);GZIPInputStream gs new GZIPInputStream(fis);ObjectInputStream ois new ObjectInputStream(gs);Employee sarah (Employee) ois.readObject();Employee sam (Employee) ois.readObject();//print the records after reconstruction of statesarah.print();sam.print();ois.close();fis.close();}
}同样的思想可以用于在网络间通过socket传输的大对象。下面的代码段示例了如何在客户/服务器之间实现大对象的压缩
// write to client
GZIPOutputStream gzipout new GZIPOutputStream(socket.getOutputStream());
ObjectOutputStream oos new ObjectOutputStream(gzipout);
oos.writeObject(obj);
gzipos.finish();下面的代码段显示了客户端从服务器端接收到数据后如何将其解压:
// read from server
Socket socket new Socket(remoteServerIP, PORT);
GZIPInputStream gzipin new GZIPInputStream(socket.getInputStream());
ObjectInputStream ois new ObjectInputStream(gzipin);
Object o ois.readObject();如何对JAR文件进行操作呢
Java档案文件JAR格式是基于标准的ZIP文件格式并附有可选择的文件清单列表。如果你想要在你我的应用程序中创建JAR文件或从JAR 文件中解压缩文件可以使用java.util.jar包它提供了读写JAR文件的类。使用java.util.jar包提供的类与本文所讲述的 java.util.zip包十分相似。所以你应该能够重新编写本文的源代码如果你想使用java.util.jar包的话。
结束语
本文讨论了你可以在应用程序中使用的数据压缩与解压的应用程序接口本文的示例程序演示了如何使用java.util.zip包来压缩数据与解压缩数据。现在你可以利用这个工具在你的应用程序中实现数据的压缩与解压了。
本文也说明了如何在络传输中实现数据的压缩与解压缩以减少网络阻塞和增强你的客户/服务器模式应用程序的性能。在网络传输中实现数据的压缩只有当传输的数据量达到成百上千字节时你才会感觉到程序性能的提升如果仅仅是传递一个字符串对象对应用程序是没什么影响的。