那要如何才能做出讓winzip能夠處理的壓縮檔呢?
import java.io.*;
import org.apache.tools.zip.*;
import java.util.Enumeration;
/**
*<p>
* <b>功能:zip壓縮、解壓(支持中文文件名)</b>
*<p>
* 說明:使用Apache Ant提供的zip工具org.apache.tools.zip實現zip壓縮和解壓功能.
* 解決了由於java.util.zip包不支持漢字的問題。
*
* @author Winty
* @modifier vernon.zheng
*/
public class AntZip {
private ZipFile zipFile;
private ZipOutputStream zipOut; // 壓縮Zip
private ZipEntry zipEntry;
private static int bufSize; // size of bytes
private byte[] buf;
private int readedBytes;
// 用於壓縮中。要去除的絕對父路路徑,目的是將絕對路徑變成相對路徑。
private String deleteAbsoluteParent;
/**
*構造方法。默認緩衝區大小爲512字節。
*/
public AntZip() {
this(512);
}
/**
*構造方法。
*
* @param bufSize
* 指定壓縮或解壓時的緩衝區大小
*/
public AntZip(int bufSize) {
this.bufSize = bufSize;
this.buf = new byte[this.bufSize];
deleteAbsoluteParent = null;
}
/**
*壓縮文件夾內的所有文件和目錄。
*
* @param zipDirectory
* 需要壓縮的文件夾名
*/
public void doZip(String zipDirectory) {
File zipDir = new File(zipDirectory);
doZip(new File[] { zipDir }, zipDir.getName());
}
/**
*壓縮多個文件或目錄。可以指定多個單獨的文件或目錄。而 <code>doZip(String zipDirectory)</code>
* 則直接壓縮整個文件夾。
*
* @param files
* 要壓縮的文件或目錄組成的<code>File</code>數組。
*@param zipFileName
* 壓縮後的zip文件名,如果後綴不是".zip", 自動添加後綴".zip"。
*/
public void doZip(File[] files, String zipFileName) {
// 未指定壓縮文件名,默認爲"ZipFile"
if (zipFileName == null || zipFileName.equals(""))
zipFileName = "ZipFile";
// 添加".zip"後綴
if (!zipFileName.endsWith(".zip"))
zipFileName += ".zip";
try {
this.zipOut = new ZipOutputStream(new BufferedOutputStream(
new FileOutputStream(zipFileName)));
compressFiles(files, this.zipOut, true);
this.zipOut.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
*壓縮文件和目錄。由doZip()調用
*
* @param files
* 要壓縮的文件
*@param zipOut
* zip輸出流
*@param isAbsolute
* 是否是要去除的絕對路徑的根路徑。因爲compressFiles()
* 會遞歸地被調用,所以只用deleteAbsoluteParent不行。必須用isAbsolute來指明
* compressFiles()是第一次調用,而不是後續的遞歸調用。即如果要壓縮的路徑是
* E:\temp,那麼第一次調用時,isAbsolute=true,則deleteAbsoluteParent會記錄
* 要刪除的路徑就是E:\ ,當壓縮子目錄E:\temp\folder時,isAbsolute=false,
* 再遞歸調用compressFiles()時,deleteAbsoluteParent仍然是E:\ 。從而保證了
* 將E:\temp及其子目錄均正確地轉化爲相對目錄。這樣壓縮纔不會出錯。不然絕對 路徑E:\也會被寫入到壓縮文件中去。
*/
private void compressFiles(File[] files, ZipOutputStream zipOut,
boolean isAbsolute) throws IOException {
for (File file : files) {
if (file == null)
continue; // 空的文件對象
// 刪除絕對父路徑
if (file.isAbsolute()) {
if (isAbsolute) {
deleteAbsoluteParent = file.getParentFile()
.getAbsolutePath();
deleteAbsoluteParent = appendSeparator(deleteAbsoluteParent);
}
} else
deleteAbsoluteParent = "";
if (file.isDirectory()) {// 是目錄
compressFolder(file, zipOut);
} else {// 是文件
compressFile(file, zipOut);
}
}
}
/**
*壓縮文件或空目錄。由compressFiles()調用。
*
* @param file
* 需要壓縮的文件
*@param zipOut
* zip輸出流
*/
public void compressFile(File file, ZipOutputStream zipOut)
throws IOException {
String fileName = file.toString();
/* 去除絕對父路徑。 */
if (file.isAbsolute())
fileName = fileName.substring(deleteAbsoluteParent.length());
if (fileName == null || fileName == "")
return;
/*
* 因爲是空目錄,所以要在結尾加一個"/"。 不然就會被當作是空文件。 ZipEntry的isDirectory()方法中,目錄以"/"結尾.
* org.apache.tools.zip.ZipEntry : public boolean isDirectory() { return
* getName().endsWith("/"); }
*/
if (file.isDirectory())
fileName = fileName + "/";// 此處不能用"\\"
zipOut.putNextEntry(new ZipEntry(fileName));
// 如果是文件則需讀;如果是空目錄則無需讀,直接轉到zipOut.closeEntry()。
if (file.isFile()) {
FileInputStream fileIn = new FileInputStream(file);
while ((this.readedBytes = fileIn.read(this.buf)) > 0) {
zipOut.write(this.buf, 0, this.readedBytes);
}
fileIn.close();
}
zipOut.closeEntry();
}
/**
*遞歸完成目錄文件讀取。由compressFiles()調用。
*
* @param dir
* 需要處理的文件對象
*@param zipOut
* zip輸出流
*/
private void compressFolder(File dir, ZipOutputStream zipOut)
throws IOException {
File[] files = dir.listFiles();
if (files.length == 0)// 如果目錄爲空,則單獨壓縮空目錄。
compressFile(dir, zipOut);
else
// 如果目錄不爲空,則分別處理目錄和文件.
compressFiles(files, zipOut, false);
}
/**
*解壓指定zip文件。
*
* @param unZipFileName
* 需要解壓的zip文件名
*/
public void unZip(String unZipFileName) {
FileOutputStream fileOut;
File file;
InputStream inputStream;
try {
this.zipFile = new ZipFile(unZipFileName);
for (Enumeration entries = this.zipFile.getEntries(); entries
.hasMoreElements();) {
ZipEntry entry = (ZipEntry) entries.nextElement();
file = new File(entry.getName());
if (entry.isDirectory()) {// 是目錄,則創建之
file.mkdirs();
} else {// 是文件
// 如果指定文件的父目錄不存在,則創建之.
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
inputStream = zipFile.getInputStream(entry);
fileOut = new FileOutputStream(file);
while ((this.readedBytes = inputStream.read(this.buf)) > 0) {
fileOut.write(this.buf, 0, this.readedBytes);
}
fileOut.close();
inputStream.close();
}
}
this.zipFile.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
*解壓指定zip文件。其中"GB18030"解決中文亂碼
*
* @param unZipFileName
* 需要解壓的zip文件名
* @param outputPath
* 輸出路徑
*/
public void unZip(String unZipFileName,String outputPath) {
FileOutputStream fileOut;
File file;
InputStream inputStream;
try {
this.zipFile = new ZipFile(unZipFileName, "GB18030");
for (Enumeration entries = this.zipFile.getEntries(); entries
.hasMoreElements();) {
ZipEntry entry = (ZipEntry) entries.nextElement();
file = new File(outputPath+entry.getName());
if (entry.isDirectory()) {// 是目錄,則創建之
file.mkdirs();
} else {// 是文件
// 如果指定文件的父目錄不存在,則創建之.
File parent = file.getParentFile();
if (parent != null && !parent.exists()) {
parent.mkdirs();
}
inputStream = zipFile.getInputStream(entry);
fileOut = new FileOutputStream(file);
while ((this.readedBytes = inputStream.read(this.buf)) > 0) {
fileOut.write(this.buf, 0, this.readedBytes);
}
fileOut.close();
inputStream.close();
}
}
this.zipFile.close();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
/**
*給文件路徑或目錄結尾添加File.separator
*
* @param fileName
* 需要添加路徑分割符的路徑
*@return 如果路徑已經有分割符,則原樣返回,否則添加分割符後返回。
*/
private String appendSeparator(String path) {
if (!path.endsWith(File.separator))
path += File.separator;
return path;
}
/**
*解壓指定zip文件。
*
* @param unZipFile
* 需要解壓的zip文件對象
*/
public void unZip(File unZipFile) {
unZip(unZipFile.toString());
}
/**
*設置壓縮或解壓時緩衝區大小。
*
* @param bufSize
* 緩衝區大小
*/
public void setBufSize(int bufSize) {
this.bufSize = bufSize;
}
// 主函數,用於測試AntZip類
/*
* public static void main(String[] args)throws Exception{
* if(args.length>=2){ AntZip zip = new AntZip();
*
* if(args[0].equals("-zip")){ //將後續參數全部轉化爲File對象 File[] files = new File[
* args.length - 1]; for(int i = 0;i < args.length - 1; i++){ files = new
* File(args[i + 1]); }
*
* //將第一個文件名作爲zip文件名 zip.doZip(files , files[0].getName());
*
* return ; } else if(args[0].equals("-unzip")){ zip.unZip(args[1]); return
* ; } }
*
* System.out.println("Usage:");
* System.out.println("壓縮:java AntZip -zip [directoryName | fileName]... ");
* System.out.println("解壓:java AntZip -unzip fileName.zip"); }
*/
}
一、ZipOutputStream.java
1.從jdk的src.zip取得ZipOutputStream.java原始碼,另存新檔存到c:/java/util/zip這個資料夾裏,檔名改爲CZipOutputStream.java。
2.開始修改原始碼,將class名稱改爲CZipOutputStream
3.建構式也必須更改爲CZipOutputStream
4.新增member,這個member記錄編碼方式
private String encoding="UTF-8";
5.再新增一個建構式(這個建構式可以讓這個class在new的時候,設定檔名的編碼)
public CZipOutputStream(OutputStream out,String encoding) {
super(out, new Deflater(Deflater.DEFAULT_COMPRESSION, true));
usesDefaultDeflater = true;
this.encoding=encoding;
}
6.找到byte[] nameBytes = getUTF8Bytes(e.name);(有二個地方),將它修改如下:
byte[] nameBytes = null;
try
{
if (this.encoding.toUpperCase().equals("UTF-8"))
nameBytes =getUTF8Bytes(e.name);
else
nameBytes= e.name.getBytes(this.encoding);
}
catch(Exception byteE)
{
nameBytes=getUTF8Bytes(e.name);
}
7.將檔案儲存在c:/java/util/zip這個資料夾內,請記得一定要有這個路徑結構,
才能把CZipOutputStream.class放在正確的package結構裏
二、ZipInputStream.java
1.從jdk的src.zip取得ZipInputStream.java原始碼,另存新檔存到c:/java/util/zip這個資料夾裏,檔名改爲CZipInputStream.java。
2.開始修改原始碼,將class名稱改爲CZipInputStream
3.建構式也必須更改爲CZipInputStream
4.新增member,這個member記錄編碼方式
private String encoding="UTF-8";
5.再新增一個建構式如下(這個建構式可以讓這個class在new的時候,設定檔名的編碼)
public CZipInputStream(InputStream in,String encoding) {
super(new PushbackInputStream(in,512),new Inflater(true),512);
usesDefaultInflater = true;
if(in == null) {
throw new NullPointerException("in is null");
}
this.encoding=encoding;
}
6.找到ZipEntry e = createZipEntry(getUTF8String(b, 0, len));這一行,將它改成如下:
ZipEntry e=null;
try
{
if (this.encoding.toUpperCase().equals("UTF-8"))
e=createZipEntry(getUTF8String(b, 0, len));
else
e=createZipEntry(new String(b,0,len,this.encoding));
}
catch(Exception byteE)
{
e=createZipEntry(getUTF8String(b, 0, len));
}
7.將檔案儲存在c:/java/util/zip這個資料夾內,請記得一定要有這個路徑結構,才能把CZipInputStream.class放在正確的package結構裏
以上兩個檔案儲存後compile產生CZipOutputStream.class及CZipInputStream.class,使用winzip開啓[java_home]/jre/lib/rt.jar這個檔案,將CZipOutputStream.class及CZipInputStream.class加進去,記得「Save full path info」一定要打勾。
以後當壓縮及解壓縮時有中文檔名及路徑的問題時,就可以指定編碼方式來處理了。
CZipOutputStream zos=new CZipOutputStream(OutputStream os,String encoding);
CZipInputStream zins=new CZipInputStream(InputStream ins,String encoding);
以「壓縮與解壓縮(1)」爲例:FileOutputStream fos =new FileOutputStream(request.getRealPath("/")+"myzip.zip");
CZipOutputStream zos=new CZipOutputStream(fos,"GBK");
其他地方都不用改,便可以處理中文檔名的壓縮。