JAVA中的壓縮與解壓縮2
2007-10-13 01:01

例程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),系統(tǒng)默認的存儲方式為壓縮方式(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算法要有一定的優(yōu)勢;但在數據可信度方面,CRC32算法則要更勝一籌。正所謂,"魚與熊掌,不可兼得。",大家只好在不同的場合下,加以取舍了。GetValue方法可以用來獲得當前的checksum值,reset方法能夠重新設置checksum為其缺省的值。

求和校驗一般用來校驗文件和信息是否正確的傳送。舉個例子,假設你想創(chuàng)建一個ZIP文件,然后將其傳送到遠程計算機上。當到達遠程計算機后,你就可以使用checksum檢驗在傳輸過程中文件是否發(fā)生錯誤。為了演示如何創(chuàng)建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來創(chuàng)建一個壓縮檔案(程序會計算出checksum值并顯示在屏幕上),然后運行UnZip類來解壓縮這個檔案(屏幕上同樣會打印出一個checksum值)。兩個值必須完全相同,否則說明出錯了。Checksums在數據校驗方面非常有用。例如,你可以創(chuàng)建一個ZIP文件,然后連同checksum值一同傳遞給你的朋友。你的朋友解壓縮文件后,將生成的checksum值與你提供的作一比較,如果相同則說明在傳遞過程中沒有發(fā)生錯誤。

壓縮對象

我們已經看到如何將文件中的數據壓縮并將其歸檔。但如果你想壓縮的數據不在文件中時,應該怎么辦呢?假設有這樣一個例子,你通過套接字(socket)來傳遞一個大對象。為了提高應用程序的性能,你可能在通過網絡開始傳遞前將數據壓縮,然后在目的地將其解壓縮。另外一個例子,我們假設你想將一個對象用壓縮格式存儲在磁碟上,ZIP格式是基于記錄方式的,不適合這項工作。GZIP更適合用來實現這種對單一數據流的操作。現在,我們來示例一下,如果在寫入磁碟前將數據壓縮,并在讀出時將數據解壓縮。示例程序6是一個在單一JVM(java虛擬機)實現了Serializable接口的簡單類,我們想要串行化該類的實例。

例程6源代碼:

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


現在,寫另外一個類來創(chuàng)建兩個從Employee類實例化而來的對象。示例程序7從Employee類創(chuàng)建了兩個對象(sarah和sam)。然后將它們的狀態(tài)以壓縮的格式存儲在一個文件中。

示例程序7源代碼

SaveEmployee.java
                                     import java.io.*;
                                     import java.util.zip.*;
                                     public class SaveEmployee {
                                     public static void main(String argv[]) throws
                                     Exception {
                                     // create some objects
                                     Employee sarah = new Employee("S. Jordan", 28,
                                     56000);
                                     Employee sam = new Employee("S. McDonald", 29,
                                     58000);
                                     // serialize the objects sarah and sam
                                     FileOutputStream 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類是用來重新構建兩個對象的狀態(tài)。一但構建成功,就調用print方法將其打印出來。

示例程序8源代碼:

ReadEmployee.java
                                     import java.io.*;
                                     import java.util.zip.*;
                                     public class ReadEmployee {
                                     public static void main(String argv[]) throws
                                     Exception{
                                     //deserialize objects sarah and sam
                                     FileInputStream 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 state
                                     sarah.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文件格式,并附有可選擇的文件清單列表。如果你想要在你我的應用程序中創(chuàng)建JAR文件或從JAR文件中解壓縮文件,可以使用java.util.jar包,它提供了讀寫JAR文件的類。使用java.util.jar包提供的類與本文所講述的java.util.zip包十分相似。所以你應該能夠重新編寫本文的源代碼,如果你想使用java.util.jar包的話。

結束語

本文討論了你可以在應用程序中使用的數據壓縮與解壓的應用程序接口,本文的示例程序演示了如何使用java.util.zip包來壓縮數據與解壓縮數據。現在你可以利用這個工具在你的應用程序中實現數據的壓縮與解壓了。

本文也說明了如何在絡傳輸中實現數據的壓縮與解壓縮,以減少網絡阻塞和增強你的客戶/服務器模式應用程序的性能。在網絡傳輸中實現數據的壓縮,只有當傳輸的數據量達到成百上千字節(jié)時,你才會感覺到程序性能的提升,如果僅僅是傳遞一個字符串對象,對應用程序是沒什么影響的。