1. 程式人生 > >使用GZIP和Zip壓縮Java資料流(轉)

使用GZIP和Zip壓縮Java資料流(轉)

本文通過對資料壓縮演算法的簡要介紹,然後以詳細的示例演示了利用java.util.zip包實現資料的壓縮與解壓,並擴充套件到在網路傳輸方面如何應用java.util.zip包現資料壓縮與解壓

綜述

許多資訊資料都或多或少的包含一些多餘的資料。通常會導致在客戶端與伺服器之間,應用程式與計算機之間極大的資料傳輸量。最常見的解決資料儲存和信 息傳送的方法是安裝額外的儲存裝置和擴充套件現有的通訊能力。這樣做是可以的,但無疑會增加組織的運作成本。一種有效的解決資料儲存與資訊傳輸的方法是通過更 有效率的程式碼來儲存資料。這篇文章簡要的介紹了資料的壓縮與解壓縮,並展示了用java.util.zip包來實現資料的壓縮與解壓縮是多麼的方便與高 效。

當然用諸如WinZip,gzip,和Java壓縮(或jar)之類的工具也可以實現資料的壓縮與解壓縮,這些工具都是獨立的應用程式。你也可以在 JAVA應用程式中呼叫這些工具,但這並不是最直接的方法,也不是有效的解決方法。尤其是你想更快速地實現資料的壓縮與解壓縮(例如在傳輸資料到遠端機器 之前)。這篇文章包括以下內容:

  • 給出一個關於資料壓縮的簡單的介紹
  • 描述java.util.zip包
  • 示例如何使用該包實現資料的壓縮與解壓縮
  • 示例如何壓縮序列化的物件並將其儲存在磁碟上
  • 示例如何通過資料壓縮來增強"客戶/服務"應用程式的效能

檔案中資料冗餘的最簡單的型別是"字元的複製"。讓我們先來看下面一個字串:

  • JJJJJJAAAAVVVVAAAAAA 這個字串可以用更簡潔的方式來編碼,那就是通過替換每一個重複的字串為單個的例項字元加上記錄重複次數的數字來表示,上面的字串可以被編碼為下面的形式:

  • 6J4A4V6A 在這裡,"6J"意味著6個字元J,"4A"意味著4個字元A,以此類推。這種字串壓縮方式稱為"行程長度編碼"方式,簡稱RLE。

再舉一個例子,考慮一下矩形影象的儲存。一個單色點陣圖,可以被儲存為下面這種形式,如圖1所示。


圖1:RLE方式下的點陣圖資訊

另外一種方式是將影象存為一個圖元檔案:

Rectangle 11, 3, 20, 5

上面的表示方法是講矩形的起始座標是(11,3),寬度是20,高度是5。

上述的矩形影象可以使用RLE編碼方式壓縮,通過對相同位記數表示如下:

0, 40
0, 40
0,10 1,20 0,10
0,10 1,1 0,18 1,1 0,10
0,10 1,1 0,18 1,1 0,10
0,10 1,1 0,18 1,1 0,10
0,10 1,20 0,10
0,40

上面第一行是講影象的第一行由40個0組成。第三行是講影象的第三行是由10個0加上20個1再加上10個0組成,其它行以此類推。

大家注意,RLE方法需要將其表示的檔案與編碼檔案分開。所以,這種方法不能應用於所有的檔案。其它的壓縮技術包括變長編碼(也被稱為哈夫曼編碼),還有其它的方法。要想了解更詳細的資訊,請參考有關資料和影象壓縮技術方面的圖書,一定會有收穫的。

資料壓縮有很多益處。不管怎麼說,最主要的好處就是減少儲存方面的需求。同樣的,對於資料通訊來講,壓縮資料在媒體中的將導致資訊傳輸資料的提升。 資料的壓縮能夠通過軟體在現有的硬體裝置上實現或者通過帶有壓縮技術的特殊的硬體裝置來實現。圖表2顯示了基本的資料壓縮結構圖。


圖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 一個輸入流,儲存著被讀取資料的Checksum
CheckedOutputStream 一個輸出流,儲存著被讀取資料的Checksum
CRC32 使用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)標準的一部分開發的,是不受專利保護的。

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。


例程1原始碼:
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 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檔案序列(簡單地說就是讀出這個ZIP檔案壓縮了多少檔案),而類ZipFile使用內嵌的隨機檔案訪問機制讀出其中的檔案內容,所以不必順序的讀出ZIP壓縮檔案序列。

注意:ZIPInputStream和ZipFile之間另外一個基本的不同點在於高速緩衝的使用方面。當檔案 使用ZipInputStream和FileInputStream流讀出的時候,ZIP條目不使用高速緩衝。然而,如果使用ZipFile(檔名)來 開啟檔案,它將使用內嵌的高速緩衝,所以如果ZipFile(檔名)被重複呼叫的話,檔案只被開啟一次。緩衝值在第二次開啟進使用。如果你工作在 UNIX系統下,這是什麼作用都沒有的,因為使用ZipFile開啟的所有ZIP檔案都在記憶體中存在對映,所以使用ZipFile的效能優於 ZipInputStream。然而,如果同一ZIP檔案的內容在程式執行期間經常改變,或是過載的話,使用ZipInputStream就成為你的首選 了。

下面顯示了使用類ZipFile來解壓一個ZIP檔案的過程:

  1. 通過指定一個被讀取的ZIP檔案,或者是檔名,或者是一個檔案物件來建立一個ZipFile物件: ZipFile zipfile = new ZipFile("figs.zip");

  2. 使 用entries方法,返回一個列舉物件,迴圈獲得檔案的ZIP條目物件: while(e.hasMoreElements()) { entry = (ZipEntry) e.nextElement(); // read contents and save them }

  3. ZIP 條目作為引數傳遞給getInputStream方法,可以讀取ZIP檔案中指定條目的內容,能過其返回的輸入流(InputStram)物件可以方便的 讀出ZIP條目的內容: is = new BufferedInputStream(zipfile.getInputStream(entry));

  4. 獲 取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); }

  5. 最後關閉所有的輸入輸出流 dest.flush(); dest.close(); is.close();

完整的程式程式碼如例程2所示。再次編譯這個檔案,並傳遞一個ZIP格式的檔案做為引數:

prompt> java UnZip2 somefile.zip

例程2原始碼:(轉載的地方沒有,自己簡單寫了一個,程式碼規範不嚴格)
package com.test.zip;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

public class ZipFileTest {

	/**
	 * 程式主入口
	 * @param args
	 */
	public static void main(String[] args) throws Exception{
		ZipFile zipFile = new ZipFile("C:\\Documents and Settings\\Administrator\\桌面\\Test.zip");
		BufferedInputStream bis = null;
		//建立輸出流
		int buffer = 2048;
		int count = 0;
		byte[] data = new byte[buffer];
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		ZipEntry entry = null;
		Enumeration<ZipEntry> enumeration = (Enumeration<ZipEntry>) zipFile.entries();
		while (enumeration.hasMoreElements()) {
			entry = enumeration.nextElement();
			bis = new BufferedInputStream(zipFile.getInputStream(entry));
			//讀取壓縮檔案中的一個條目,並將條目中的內容輸出到指定檔案
			fos = new FileOutputStream(entry.getName());
			bos = new BufferedOutputStream(fos,buffer);
			while ((count = bis.read(data,0,buffer))!= -1) {
				bos.write(data,0,count);
			}
			bos.flush();
		}
		bos.close();
		bis.close();
	}

}

類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原始碼:
package com.test.zip;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class ToZipTest {

	/**
	 * 程式主入口
	 * @param args
	 * @throws FileNotFoundException 
	 */
	public static void main(String[] args){
		//輸出流
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		ZipOutputStream zipos = null;
		//輸入流
		FileInputStream fis = null;
		BufferedInputStream bis = null;
		try {
			//建立輸出
			fos = new FileOutputStream("C:\\Documents and Settings\\Administrator\\桌面\\TestZip.zip");
			bos = new BufferedOutputStream(fos);
			zipos = new ZipOutputStream(bos);
			//條目列表以壓縮方式(DEFLATED)方式加入ZIP檔案中
			//zipos.setMethod(ZipOutputStream.DEFLATED);
			//條目列表以不壓縮方式(STORED)方式加入ZIP檔案中
			//zipos.setMethod(ZipOutputStream.DEFLATED);
			//建立與要壓縮的檔案列表的連線
			File fileDirectory = new File("C:\\Documents and Settings\\Administrator\\桌面\\zipFile");
			//獲取檔案列表
			String[] files = fileDirectory.list();
			ZipEntry entry = null;
			int buffer = 2048;
			int count = 0;
			byte[] data = new byte[buffer];
			for (int i = 0; i < files.length; i++) {
				String fileName = files[i];
				System.out.println("新增檔案:" + fileName);
				//建立輸入
				fis = new FileInputStream(new File(fileDirectory,fileName));
				bis = new BufferedInputStream(fis);
				//讀出的資料建立一個ZIP條目列表
				entry = new ZipEntry(fileName);
				//將ZIP條目列表寫入輸出流
				zipos.putNextEntry(entry);
				//將資料寫入ZIP檔案
				while ((count = bis.read(data,0,buffer))!= -1) {
					zipos.write(data,0,count);
				}
				zipos.flush();
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				zipos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				bis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
	}
}

注意: 條目列表可以以兩種方式加入ZIP檔案中,一種是壓縮方式(DEFLATED),另一種是不壓縮方式(STORED),系統預設的儲存方式為壓縮方式 (DEFLATED)。SetMethod方法可以用來設定它的儲存方式。例如,設定儲存方式為DEFLATED(壓縮)應該這樣做: out.setMethod(ZipOutputStream.DEFLATED) 設定儲存方式為(不壓縮)應該這樣做: out.setMethod(ZipOutputStream.STORED)。


類ZipEntry描述了儲存在ZIP檔案中的壓縮檔案。類中包含有多種方法可以用來設定和獲得ZIP條目的資訊。類ZipEntry是被 ZipFile和ZipInputStream使用來讀取ZIP檔案,ZipOutputStream來寫入ZIP檔案的。ZipEntry中最有用的一 些方法顯示在下面的表格2中,並且有相應的描述。

方法簽名 描述
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原始碼:
<pre name="code" class="java">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值與你提供的作一比較,如果相同則說 明在傳遞過程中沒有發生錯誤。

壓縮物件

我們已經看到如何將檔案中的資料壓縮並將其歸檔。但如果你想壓縮的資料不在檔案中時,應該怎麼辦呢?假設有這樣一個例子,你通過套接字 (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);
  }
}

現在,寫另外一個類來建立兩個從Employee類例項化而來的物件。示例程式7從Employee類建立了兩個物件(sarah和sam)。然後將它們的狀態以壓縮的格式儲存在一個檔案中。


示例程式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類是用來重新構建兩個物件的狀態。一但構建成功,就呼叫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();

Java檔案檔案(JAR)格式是基於標準的ZIP檔案格式,並附有可選擇的檔案清單列表。如果你想要在你我的應用程式中建立JAR檔案或從JAR 檔案中解壓縮檔案,可以使用java.util.jar包,它提供了讀寫JAR檔案的類。使用java.util.jar包提供的類與本文所講述的 java.util.zip包十分相似。所以你應該能夠重新編寫本文的原始碼,如果你想使用java.util.jar包的話。

結束語

本文討論了你可以在應用程式中使用的資料壓縮與解壓的應用程式介面,本文的示例程式演示瞭如何使用java.util.zip包來壓縮資料與解壓縮資料。現在你可以利用這個工具在你的應用程式中實現資料的壓縮與解壓了。

本文也說明了如何在絡傳輸中實現資料的壓縮與解壓縮,以減少網路阻塞和增強你的客戶/伺服器模式應用程式的效能。在網路傳輸中實現資料的壓縮,只有當傳輸的資料量達到成百上千位元組時,你才會感覺到程式效能的提升,如果僅僅是傳遞一個字串物件,對應用程式是沒什麼影響的。