溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

利用JAVA API函數(shù)實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓縮(轉(zhuǎn))

發(fā)布時(shí)間:2020-08-07 05:23:37 來源:ITPUB博客 閱讀:253 作者:rainytag 欄目:編程語言
本文通過對(duì)數(shù)據(jù)壓縮算法的簡(jiǎn)要介紹,然后以詳細(xì)的示例演示了利用java.util.zip包實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓,并擴(kuò)展到在網(wǎng)絡(luò)傳輸方面如何應(yīng)用java.util.zip包現(xiàn)數(shù)據(jù)壓縮與解壓
綜述

許多信息資料都或多或少的包含一些多余的數(shù)據(jù)。通常會(huì)導(dǎo)致在客戶端與服務(wù)器之間,應(yīng)用程序與計(jì)算機(jī)之間極大的數(shù)據(jù)傳輸量。最常見的解決數(shù)據(jù)存儲(chǔ)和信息傳送的方法是安裝額外的存儲(chǔ)設(shè)備和擴(kuò)展現(xiàn)有的通訊能力。這樣做是可以的,但無疑會(huì)增加組織的運(yùn)作成本。一種有效的解決數(shù)據(jù)存儲(chǔ)與信息傳輸?shù)姆椒ㄊ峭ㄟ^更有效率的代碼來存儲(chǔ)數(shù)據(jù)。這篇文章簡(jiǎn)要的介紹了數(shù)據(jù)的壓縮與解壓縮,并展示了用java.util.zip包來實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓縮是多么的方便與高效。

當(dāng)然用諸如WinZip,gzip,和Java壓縮(或jar)之類的工具也可以實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓縮,這些工具都是獨(dú)立的應(yīng)用程序。你也可以在JAVA應(yīng)用程序中調(diào)用這些工具,但這并不是最直接的方法,也不是有效的解決方法。尤其是你想更快速地實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓縮(例如在傳輸數(shù)據(jù)到遠(yuǎn)程機(jī)器之前)。這篇文章包括以下內(nèi)容:

給出一個(gè)關(guān)于數(shù)據(jù)壓縮的簡(jiǎn)單的介紹
描述java.util.zip包
示例如何使用該包實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓縮
示例如何壓縮串行化的對(duì)象并將其存儲(chǔ)在磁碟上
示例如何通過數(shù)據(jù)壓縮來增強(qiáng)"客戶/服務(wù)"應(yīng)用程序的性能
數(shù)據(jù)壓縮概述

文件中數(shù)據(jù)冗余的最簡(jiǎn)單的類型是"字符的復(fù)制"。讓我們先來看下面一個(gè)字符串:

JJJJJJAAAAVVVVAAAAAA
這個(gè)字符串可以用更簡(jiǎn)潔的方式來編碼,那就是通過替換每一個(gè)重復(fù)的字符串為單個(gè)的實(shí)例字符加上記錄重復(fù)次數(shù)的數(shù)字來表示,上面的字符串可以被編碼為下面的形式:

6J4A4V6A
在這里,"6J"意味著6個(gè)字符J,"4A"意味著4個(gè)字符A,以此類推。這種字符串壓縮方式稱為"行程長(zhǎng)度編碼"方式,簡(jiǎn)稱RLE。

再舉一個(gè)例子,考慮一下矩形圖像的存儲(chǔ)。一個(gè)單色位圖,可以被存儲(chǔ)為下面這種形式,如圖1所示。


圖1:RLE方式下的位圖信息

另外一種方式是將圖像存為一個(gè)圖元文件:

Rectangle 11, 3, 20, 5

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

上述的矩形圖像可以使用RLE編碼方式壓縮,通過對(duì)相同位記數(shù)表示如下:

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個(gè)0組成。第三行是講圖像的第三行是由10個(gè)0加上20個(gè)1再加上10個(gè)0組成,其它行以此類推。

大家注意,RLE方法需要將其表示的文件與編碼文件分開。所以,這種方法不能應(yīng)用于所有的文件。其它的壓縮技術(shù)包括變長(zhǎng)編碼(也被稱為哈夫曼編碼),還有其它的方法。要想了解更詳細(xì)的信息,請(qǐng)參考有關(guān)數(shù)據(jù)和圖像壓縮技術(shù)方面的圖書,一定會(huì)有收獲的。

數(shù)據(jù)壓縮有很多益處。不管怎么說,最主要的好處就是減少存儲(chǔ)方面的需求。同樣的,對(duì)于數(shù)據(jù)通信來講,壓縮數(shù)據(jù)在媒體中的將導(dǎo)致信息傳輸數(shù)據(jù)的提升。數(shù)據(jù)的壓縮能夠通過軟件在現(xiàn)有的硬件設(shè)備上實(shí)現(xiàn)或者通過帶有壓縮技術(shù)的特殊的硬件設(shè)備來實(shí)現(xiàn)。圖表2顯示了基本的數(shù)據(jù)壓縮結(jié)構(gòu)圖。


圖2:數(shù)據(jù)壓縮結(jié)構(gòu)圖

ZIP VS GZIP

如果你是在Windows系統(tǒng)下工作,你可能會(huì)對(duì)工具WinZip很熟悉,是用來創(chuàng)建壓縮檔案和解開壓縮檔案的。而在UNIX平臺(tái)上,會(huì)有一些不同,命令tar用來創(chuàng)建一個(gè)檔案文件(并不壓縮),其它的程序(gzip或compress)用來創(chuàng)建一個(gè)壓縮檔案。

WinZip和PkZip之類的工具同時(shí)扮演著歸檔和壓縮兩個(gè)角色。他們將文件壓縮并將其歸檔。另一方面,gzip并不將文件歸檔。所以,在UNIX平臺(tái)上,命令tar通常用來創(chuàng)建一個(gè)檔案文件,然后命令gzip來將檔案文件壓縮。

Java.util.zip包

Java提供了java.util.zip包用來兼容ZIP格式的數(shù)據(jù)壓縮。它提供了一系列的類用來讀取,創(chuàng)建,修改ZIP和GZIP格式的文件。它還提供了工具類來計(jì)算任意輸入流的數(shù)目,這可以用來驗(yàn)證輸入數(shù)據(jù)的有效性。該包提供了一個(gè)接口,十四個(gè)類,和兩個(gè)異常處理類,如表1所示。

表1: java.util.zip包

條目 類型 描述
Checksum 接口 被類Adler32和CRC32實(shí)現(xiàn)的接口
Adler32 類 使用Alder32算法來計(jì)算Checksum數(shù)目
CheckedInputStream 類 一個(gè)輸入流,保存著被讀取數(shù)據(jù)的Checksum
CheckedOutputStream 類 一個(gè)輸出流,保存著被讀取數(shù)據(jù)的Checksum
CRC32 類 使用CRC32算法來計(jì)算Checksum數(shù)目
Deflater 類 使用ZLIB壓縮類,支持通常的壓縮方式
DeflaterOutputStream 類 一個(gè)輸出過濾流,用來壓縮Deflater格式數(shù)據(jù)
GZIPInputStream 類 一個(gè)輸入過濾流,讀取GZIP格式壓縮數(shù)據(jù)
GZIPOutputStream 類 一個(gè)輸出過濾流,讀取GZIP格式壓縮數(shù)據(jù)
Inflater 類 使用ZLIB壓縮類,支持通常的解壓方式
InlfaterInputStream 類 一個(gè)輸入過濾流,用來解壓Inlfater格式的壓縮數(shù)據(jù)
ZipEntry 類 存儲(chǔ)ZIP條目
ZipFile 類 從ZIP文件中讀取ZIP條目
ZipInputStream 類 一個(gè)輸入過濾流,用來讀取ZIP格式文件中的文件
ZipOutputStream 類 一個(gè)輸出過濾流,用來向ZIP格式文件口寫入文件
DataFormatException 異常類 拋出一個(gè)數(shù)據(jù)格式錯(cuò)誤
ZipException 異常類 拋出一個(gè)ZIP文件



注意:ZLIB壓縮類最初是作為可移植的網(wǎng)絡(luò)圖像文件格式(PNG)標(biāo)準(zhǔn)的一部分開發(fā)的,是不受專利保護(hù)的。

從ZIP文件中解壓縮和提取數(shù)據(jù)

java.util.zip包提供了數(shù)據(jù)壓縮與解壓縮所需要的類。ZIP文件的解壓縮實(shí)質(zhì)上就是從輸入流中讀取數(shù)據(jù)。Java.util.zip包提供了類ZipInputStream來讀取ZIP文件。ZipInputStream流的創(chuàng)建與其它輸入流的創(chuàng)建沒什么兩樣。舉個(gè)例子,下面的代碼段創(chuàng)建了一個(gè)輸入流來讀取ZIP格式的文件:

FileInputStream fis = new FileInputStream("figs.zip");
ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fis));






ZIP輸入流打開后,你可以使用getNextEntry方法來讀取ZIP文件中的條目數(shù),該方法返回一個(gè)ZipEntry對(duì)象。如果到達(dá)文件的尾部,getNextEntry返回null:

ZipEntry entry;
while((entry = zin.getNextEntry()) != null) {
// extract data
// open output streams
}






現(xiàn)在,你應(yīng)該建立一個(gè)輸出流,如下所示:

int BUFFER = 2048;
FileOutputStream fos = new FileOutputStream(entry.getName());
BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER);






注意:在這段代碼中我們用BufferedOutputStream代替了ZIPOutputStream。ZIPOutputStream和GZIPOutputStream使用內(nèi)置的512字節(jié)緩沖。當(dāng)緩沖區(qū)的大小大于512字節(jié)時(shí),使用BufferedOutputStream才是正確的(例子中設(shè)置為2048)。ZIPOutputStream不允許你設(shè)置緩沖區(qū)的大小,GZIPOutputStream也是一樣,但創(chuàng)建 GZIPOutputStream 對(duì)象時(shí)可以通過構(gòu)造函數(shù)的參數(shù)指定內(nèi)置的緩沖尺寸。

這段代碼中,使用ZIP內(nèi)含的條目名稱創(chuàng)建一個(gè)文件輸出流??梢允褂胑ntry.getName來得到它的返回句柄。接著讀出被壓縮的源數(shù)據(jù),然后寫入輸出流:

while ((count = zin.read(data, 0, BUFFER)) != -1) {
//System.out.write(x);
dest.write(data, 0, count);
}






最后,不要忘記關(guān)閉輸入和輸出流:

dest.flush();
dest.close();
zin.close();






例程1的源程序UnZip.java顯示如何解壓縮并從ZIP檔案中將文件釋放出來。測(cè)試這個(gè)例子,編譯這個(gè)類,并運(yùn)行它,傳給它一個(gè)ZIP格式的文件作為參數(shù):

prompt> java UnZip somefile.zip

注意:somefile.zip應(yīng)該是一個(gè)ZIP壓縮檔案,可以用任何一種ZIP壓縮工具來創(chuàng)建,例如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();
}
}
}






有一點(diǎn)值得大家注意,類ZipInputStream讀出ZIP文件序列(簡(jiǎn)單地說就是讀出這個(gè)ZIP文件壓縮了多少文件),而類ZipFile使用內(nèi)嵌的隨機(jī)文件訪問機(jī)制讀出其中的文件內(nèi)容,所以不必順序的讀出ZIP壓縮文件序列。

注意:ZIPInputStream和ZipFile之間另外一個(gè)基本的不同點(diǎn)在于高速緩沖的使用方面。當(dāng)文件使用ZipInputStream和FileInputStream流讀出的時(shí)候,ZIP條目不使用高速緩沖。然而,如果使用ZipFile(文件名)來打開文件,它將使用內(nèi)嵌的高速緩沖,所以如果ZipFile(文件名)被重復(fù)調(diào)用的話,文件只被打開一次。緩沖值在第二次打開進(jìn)使用。如果你工作在UNIX系統(tǒng)下,這是什么作用都沒有的,因?yàn)槭褂肸ipFile打開的所有ZIP文件都在內(nèi)存中存在映射,所以使用ZipFile的性能優(yōu)于ZipInputStream。然而,如果同一ZIP文件的內(nèi)容在程序執(zhí)行期間經(jīng)常改變,或是重載的話,使用ZipInputStream就成為你的首選了。

下面顯示了使用類ZipFile來解壓一個(gè)ZIP文件的過程:

通過指定一個(gè)被讀取的ZIP文件,或者是文件名,或者是一個(gè)文件對(duì)象來創(chuàng)建一個(gè)ZipFile對(duì)象:
ZipFile zipfile = new ZipFile("figs.zip");

使用entries方法,返回一個(gè)枚舉對(duì)象,循環(huán)獲得文件的ZIP條目對(duì)象:
while(e.hasMoreElements()) {
entry = (ZipEntry) e.nextElement();
// read contents and save them
}

ZIP條目作為參數(shù)傳遞給getInputStream方法,可以讀取ZIP文件中指定條目的內(nèi)容,能過其返回的輸入流(InputStram)對(duì)象可以方便的讀出ZIP條目的內(nèi)容:
is = new BufferedInputStream(zipfile.getInputStream(entry));

獲取ZIP條目的文件名,創(chuàng)建輸出流,并保存:
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);
}

最后關(guān)閉所有的輸入輸出流 dest.flush();
dest.close();
is.close();

完整的程序代碼如例程2所示。再次編譯這個(gè)文件,并傳遞一個(gè)ZIP格式的文件做為參數(shù):

prompt> java UnZip2 somefile.zip

例程2源碼:

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






將數(shù)據(jù)壓縮歸檔入一ZIP文件

類ZipOutputStream能夠用來將數(shù)據(jù)壓縮成一個(gè)ZIP文件。ZipOutputStream將數(shù)據(jù)寫入ZIP格式的輸出流。下面的步驟與創(chuàng)建一個(gè)ZIP文件相關(guān)。

1、第一步是創(chuàng)建一個(gè)ZipOutputStream對(duì)象,我們將要寫入輸出流的文件作為參數(shù)傳給它。下面的代碼演示了如何創(chuàng)建一個(gè)名為"myfigs.zip"的ZIP文件。
FileOutputStream dest = new
FileOutputStream("myfigs.zip");
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));

2、一但目標(biāo)輸出流創(chuàng)建后,下一步就是打開數(shù)據(jù)源文件。在這個(gè)例子中,源數(shù)據(jù)文件是指那些當(dāng)前目錄下的文件。命令list用來得到當(dāng)前目錄下文件列表:

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
}






注意:這個(gè)例程能夠壓縮當(dāng)前目錄下的所有文件。它不能處理子目錄。作為一個(gè)練習(xí),你可以修改例程3來處理子目錄。

3、 為讀出的數(shù)據(jù)創(chuàng)建一個(gè)ZIP條目列表:
ZipEntry entry = new ZipEntry(files[i]))

4、 在你將數(shù)據(jù)寫入ZIP輸出流之前,你必須使用putNextEntry方法將ZIP條目列表寫入輸出流:
out.putNextEntry(entry);

5、 將數(shù)據(jù)寫入ZIP文件:
int count;
while((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}

6、 最后關(guān)閉所有的輸入輸出流:
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:zipmyfigs.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)默認(rèn)的存儲(chǔ)方式為壓縮方式(DEFLATED)。SetMethod方法可以用來設(shè)置它的存儲(chǔ)方式。例如,設(shè)置存儲(chǔ)方式為DEFLATED(壓縮)應(yīng)該這樣做: out.setMethod(ZipOutputStream.DEFLATED) 設(shè)置存儲(chǔ)方式為(不壓縮)應(yīng)該這樣做: out.setMethod(ZipOutputStream.STORED)。

ZIP文件屬性

類ZipEntry描述了存儲(chǔ)在ZIP文件中的壓縮文件。類中包含有多種方法可以用來設(shè)置和獲得ZIP條目的信息。類ZipEntry是被ZipFile和ZipInputStream使用來讀取ZIP文件,ZipOutputStream來寫入ZIP文件的。ZipEntry中最有用的一些方法顯示在下面的表格2中,并且有相應(yīng)的描述。
表格 2: 類ZipEntry中一些有用的方法

方法簽名 描述
public String getComment() 返回條目的注釋, 沒有返回null
public long getCompressedSize() 返回條目壓縮后的大小, 未知返回-1
public int getMethod() 返回條目的壓縮方式,沒有指定返回 -1
public String getName() 返回條目的名稱
public long getSize() 返回未被壓縮的條目的大小,未知返回-1
public long getTime() 返回條目的修改時(shí)間, 沒有指定返回-1
public void setComment(String c) 設(shè)置條目的注釋
public void setMethod(int method) 設(shè)置條目的壓縮方式
public void setSize(long size) 設(shè)置沒有壓縮的條目的大小
public void setTime(long time) 設(shè)置條目的修改時(shí)間



求和校驗(yàn)

java.util.zip包中另外一些比較重要的類是Adler32和CRC32,它們實(shí)現(xiàn)了java.util.zip.Checksum接口,并估算了壓縮數(shù)據(jù)的校驗(yàn)和(checksum)。眾所周知,在運(yùn)算速度方面,Adler32算法比CRC32算法要有一定的優(yōu)勢(shì);但在數(shù)據(jù)可信度方面,CRC32算法則要更勝一籌。正所謂,"魚與熊掌,不可兼得。",大家只好在不同的場(chǎng)合下,加以取舍了。GetValue方法可以用來獲得當(dāng)前的checksum值,reset方法能夠重新設(shè)置checksum為其缺省的值。

求和校驗(yàn)一般用來校驗(yàn)文件和信息是否正確的傳送。舉個(gè)例子,假設(shè)你想創(chuàng)建一個(gè)ZIP文件,然后將其傳送到遠(yuǎn)程計(jì)算機(jī)上。當(dāng)?shù)竭_(dá)遠(yuǎn)程計(jì)算機(jī)后,你就可以使用checksum檢驗(yàn)在傳輸過程中文件是否發(fā)生錯(cuò)誤。為了演示如何創(chuàng)建checksums,我們修改了例程1和例程3,在例程4和例程5中使用了兩個(gè)新類,一個(gè)是CheckedInputStream,另一個(gè)是CheckedOutputStream。(大家注意:這兩段代碼在壓縮與解壓縮過程中,使用了同一種算法,求數(shù)據(jù)的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:zipmyfigs.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();
}
}
}






測(cè)試?yán)?和5,編譯類文件并運(yùn)行類Zip來創(chuàng)建一個(gè)壓縮檔案(程序會(huì)計(jì)算出checksum值并顯示在屏幕上),然后運(yùn)行UnZip類來解壓縮這個(gè)檔案(屏幕上同樣會(huì)打印出一個(gè)checksum值)。兩個(gè)值必須完全相同,否則說明出錯(cuò)了。Checksums在數(shù)據(jù)校驗(yàn)方面非常有用。例如,你可以創(chuàng)建一個(gè)ZIP文件,然后連同checksum值一同傳遞給你的朋友。你的朋友解壓縮文件后,將生成的checksum值與你提供的作一比較,如果相同則說明在傳遞過程中沒有發(fā)生錯(cuò)誤。

壓縮對(duì)象

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

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






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

示例程序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();
}
}






現(xiàn)在,示例程序8中的ReadEmpolyee類是用來重新構(gòu)建兩個(gè)對(duì)象的狀態(tài)。一但構(gòu)建成功,就調(diào)用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();
}
}






同樣的思想可以用于在網(wǎng)絡(luò)間通過(socket)傳輸?shù)拇髮?duì)象。下面的代碼段示例了如何在客戶/服務(wù)器之間實(shí)現(xiàn)大對(duì)象的壓縮:

// write to client
GZIPOutputStream gzipout = new
GZIPOutputStream(socket.getOutputStream());
ObjectOutputStream oos = new
ObjectOutputStream(gzipout);
oos.writeObject(obj);
gzipos.finish();






下面的代碼段顯示了客戶端從服務(wù)器端接收到數(shù)據(jù)后,如何將其解壓:

// read from server
Socket socket = new Socket(remoteServerIP, PORT);
GZIPInputStream gzipin = new
GZIPInputStream(socket.getInputStream());
ObjectInputStream ois = new ObjectInputStream(gzipin);
Object o = ois.readObject();






如何對(duì)JAR文件進(jìn)行操作呢?

Java檔案文件(JAR)格式是基于標(biāo)準(zhǔn)的ZIP文件格式,并附有可選擇的文件清單列表。如果你想要在你我的應(yīng)用程序中創(chuàng)建JAR文件或從JAR文件中解壓縮文件,可以使用java.util.jar包,它提供了讀寫JAR文件的類。使用java.util.jar包提供的類與本文所講述的java.util.zip包十分相似。所以你應(yīng)該能夠重新編寫本文的源代碼,如果你想使用java.util.jar包的話。

結(jié)束語

本文討論了你可以在應(yīng)用程序中使用的數(shù)據(jù)壓縮與解壓的應(yīng)用程序接口,本文的示例程序演示了如何使用java.util.zip包來壓縮數(shù)據(jù)與解壓縮數(shù)據(jù)。現(xiàn)在你可以利用這個(gè)工具在你的應(yīng)用程序中實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓了。

本文也說明了如何在絡(luò)傳輸中實(shí)現(xiàn)數(shù)據(jù)的壓縮與解壓縮,以減少網(wǎng)絡(luò)阻塞和增強(qiáng)你的客戶/服務(wù)器模式應(yīng)用程序的性能。在網(wǎng)絡(luò)傳輸中實(shí)現(xiàn)數(shù)據(jù)的壓縮,只有當(dāng)傳輸?shù)臄?shù)據(jù)量達(dá)到成百上千字節(jié)時(shí),你才會(huì)感覺到程序性能的提升,如果僅僅是傳遞一個(gè)字符串對(duì)象,對(duì)應(yīng)用程序是沒什么影響的。

更多的資料信息:
? The java.util.zip Package
? The java.util.jar Package
? Object Serialization
? Transporting Objects over Sockets
本文所演示的代碼經(jīng)本人在Win2000平臺(tái)下使用Java2SDK1.4.0調(diào)試通過,可以在這里下載。
代碼1、代碼2、代碼3、代碼4、代碼5、代碼6、代碼7、代碼8
[@more@]
向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI