zip4j -- Java處理zip壓縮文件的完整解決方案


 ZIP4J,作為解決了我的問題的終極解決方案,本來一開始在搜索引擎上就看到了它的蹤跡,但因天朝的網絡環境問題,zip4j的官網一直無法訪問,最終使我多走了好多冤枉路,期間試過JDK的zip包,試過Apache的zip解決方案,也試過如winzipaes等其它的開源框架,最終沒有滿足自己的需求,最后,我不得已掛了一下代理將zip4j下載了下來,試用了一下,果然威力無比,所到之處所向披靡...

閑話少說,如果需要可以到zip4j的官網下載該開源項目:

http://www.lingala.net/zip4j/

不過需要提醒的是可能無法直接訪問,如果無法正常訪問,請自行准備代理訪問,如果各位嫌麻煩,可以到這里下載

http://download.csdn.net/detail/zhangyihui1986/4418509

這是我的CSDN資源鏈接,下載需要3分,您如果分數不多,可以留言索取,呵呵...我也需要積分,請諒解!

官網上下載的資源好像是不帶API幫助文檔的,我利用其源碼生成了一份,也一並打在我的資源文件中,希望能幫到大家。

 

ZIP4J的官方說明

 

(自己翻譯了一下,英文不好,呵呵...)

Key features(主要特性):

 

  • Create, Add, Extract, Update, Remove files from a Zip file
    針對ZIP壓縮文件創建、添加、抽出、更新和移除文件
  • Read/Write password protected Zip files
    (讀寫有密碼保護的Zip文件)
  • Supports AES 128/256 Encryption
    (支持AES 128/256算法加密)
  • Supports Standard Zip Encryption
    (支持標准Zip算法加密)
  • Supports Zip64 format
    (支持zip64格式)
  • Supports Store (No Compression) and Deflate compression method
    (支持Store(非壓縮)和Deflate壓縮方法---不太明白)
  • Create or extract files from Split Zip files (Ex: z01, z02,...zip)
    (針對分塊zip文件創建和抽出文件)
  • Supports Unicode file names
    (支持Unicode編碼文件名)
  • Progress Monitor
    (進度監控)

從上面的主要特性可以看出,zip4j的功能是非常強大的,完全可以利用其寫個類似於好壓的zip文件管理軟件,但我們用地最多的可能還是利用其作一些簡單的解壓和壓縮操作,其它的功能極少觸碰,我也一樣,呵呵...

 

使用注意點

 

zip4j默認采用UTF-8編碼,所以它支持中文,同時也支持密碼,而且支持多種壓縮算法,可以說功能強大,但使用起來卻非常簡單,當然,如果需求比較復雜,那就得好好去研究了。如果你僅僅是簡單地解壓一個zip壓縮文件,那么只需要簡單地幾步即可:

    public static void unzip(File zipFile, String dest, String passwd) throws ZipException {  
            ZipFile zFile = new ZipFile(zipFile);  // 首先創建ZipFile指向磁盤上的.zip文件  
            zFile.setFileNameCharset("GBK");       // 設置文件名編碼,在GBK系統中需要設置  
            if (!zFile.isValidZipFile()) {   // 驗證.zip文件是否合法,包括文件是否存在、是否為zip文件、是否被損壞等  
                throw new ZipException("壓縮文件不合法,可能被損壞.");  
            }  
            File destDir = new File(dest);     // 解壓目錄  
            if (destDir.isDirectory() && !destDir.exists()) {  
                destDir.mkdir();  
            }  
            if (zFile.isEncrypted()) {  
                zFile.setPassword(passwd.toCharArray());  // 設置密碼  
            }  
            zFile.extractAll(dest);      // 將文件抽出到解壓目錄(解壓)  
        }  

當然將指定文件壓縮成zip文件也是非常簡單的事,此處不再貼代碼,如有需要請參看下面的完整示例。

 提示:如果將要解壓的壓縮文件中的文件名含有中文,解壓時需要注意一點,就是設置文件名字符集方法

zFile.setFileNameCharset("GBK"); 

這個方法的調用一定要靠前,要靠多前呢?其實最好在創建ZipFile之后就設置上,至少要在

zFile.isValidZipFile()  

這個方法調用之前調用,我在應用時因為這個問題耽誤好久,最后查看源碼才弄明白,好像是ZipFile在驗證方法中就將編碼設置好,以后就不再對文件名編碼作改變了。

 

完整示例

 

下面提供一個自己寫的例子,鄙人才疏學淺,天分也差,寫的代碼質量很差,斗膽貼上,希望能起到拋磚引玉的作用。

package com.ninemax.cul.util;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;

/**
 * ZIP壓縮文件操作工具類
 * 支持密碼
 * 依賴zip4j開源項目(http://www.lingala.net/zip4j/)
 * 版本1.3.1
 * @author ninemax
 */
public class CompressUtil {
    
    /**
     * 使用給定密碼解壓指定的ZIP壓縮文件到指定目錄
     * <p>
     * 如果指定目錄不存在,可以自動創建,不合法的路徑將導致異常被拋出
     * @param zip 指定的ZIP壓縮文件
     * @param dest 解壓目錄
     * @param passwd ZIP文件的密碼
     * @return 解壓后文件數組
     * @throws ZipException 壓縮文件有損壞或者解壓縮失敗拋出
     */
    public static File [] unzip(String zip, String dest, String passwd) throws ZipException {
        File zipFile = new File(zip);
        return unzip(zipFile, dest, passwd);
    }
    
    /**
     * 使用給定密碼解壓指定的ZIP壓縮文件到當前目錄
     * @param zip 指定的ZIP壓縮文件
     * @param passwd ZIP文件的密碼
     * @return  解壓后文件數組
     * @throws ZipException 壓縮文件有損壞或者解壓縮失敗拋出
     */
    public static File [] unzip(String zip, String passwd) throws ZipException {
        File zipFile = new File(zip);
        File parentDir = zipFile.getParentFile();
        return unzip(zipFile, parentDir.getAbsolutePath(), passwd);
    }
    
    /**
     * 使用給定密碼解壓指定的ZIP壓縮文件到指定目錄
     * <p>
     * 如果指定目錄不存在,可以自動創建,不合法的路徑將導致異常被拋出
     * @param zip 指定的ZIP壓縮文件
     * @param dest 解壓目錄
     * @param passwd ZIP文件的密碼
     * @return  解壓后文件數組
     * @throws ZipException 壓縮文件有損壞或者解壓縮失敗拋出
     */
    public static File [] unzip(File zipFile, String dest, String passwd) throws ZipException {
        ZipFile zFile = new ZipFile(zipFile);
        zFile.setFileNameCharset("GBK");
        if (!zFile.isValidZipFile()) {
            throw new ZipException("壓縮文件不合法,可能被損壞.");
        }
        File destDir = new File(dest);
        if (destDir.isDirectory() && !destDir.exists()) {
            destDir.mkdir();
        }
        if (zFile.isEncrypted()) {
            zFile.setPassword(passwd.toCharArray());
        }
        zFile.extractAll(dest);
        
        List<FileHeader> headerList = zFile.getFileHeaders();
        List<File> extractedFileList = new ArrayList<File>();
        for(FileHeader fileHeader : headerList) {
            if (!fileHeader.isDirectory()) {
                extractedFileList.add(new File(destDir,fileHeader.getFileName()));
            }
        }
        File [] extractedFiles = new File[extractedFileList.size()];
        extractedFileList.toArray(extractedFiles);
        return extractedFiles;
    }
    
    /**
     * 壓縮指定文件到當前文件夾
     * @param src 要壓縮的指定文件
     * @return 最終的壓縮文件存放的絕對路徑,如果為null則說明壓縮失敗.
     */
    public static String zip(String src) {
        return zip(src,null);
    }
    
    /**
     * 使用給定密碼壓縮指定文件或文件夾到當前目錄
     * @param src 要壓縮的文件
     * @param passwd 壓縮使用的密碼
     * @return 最終的壓縮文件存放的絕對路徑,如果為null則說明壓縮失敗.
     */
    public static String zip(String src, String passwd) {
        return zip(src, null, passwd);
    }
    
    /**
     * 使用給定密碼壓縮指定文件或文件夾到當前目錄
     * @param src 要壓縮的文件
     * @param dest 壓縮文件存放路徑
     * @param passwd 壓縮使用的密碼
     * @return 最終的壓縮文件存放的絕對路徑,如果為null則說明壓縮失敗.
     */
    public static String zip(String src, String dest, String passwd) {
        return zip(src, dest, true, passwd);
    }
    
    /**
     * 使用給定密碼壓縮指定文件或文件夾到指定位置.
     * <p>
     * dest可傳最終壓縮文件存放的絕對路徑,也可以傳存放目錄,也可以傳null或者"".<br />
     * 如果傳null或者""則將壓縮文件存放在當前目錄,即跟源文件同目錄,壓縮文件名取源文件名,以.zip為后綴;<br />
     * 如果以路徑分隔符(File.separator)結尾,則視為目錄,壓縮文件名取源文件名,以.zip為后綴,否則視為文件名.
     * @param src 要壓縮的文件或文件夾路徑
     * @param dest 壓縮文件存放路徑
     * @param isCreateDir 是否在壓縮文件里創建目錄,僅在壓縮文件為目錄時有效.<br />
     * 如果為false,將直接壓縮目錄下文件到壓縮文件.
     * @param passwd 壓縮使用的密碼
     * @return 最終的壓縮文件存放的絕對路徑,如果為null則說明壓縮失敗.
     */
    public static String zip(String src, String dest, boolean isCreateDir, String passwd) {
        File srcFile = new File(src);
        dest = buildDestinationZipFilePath(srcFile, dest);
        ZipParameters parameters = new ZipParameters();
        parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);            // 壓縮方式
        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);    // 壓縮級別
        if (!StringUtils.isEmpty(passwd)) {
            parameters.setEncryptFiles(true);
            parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_STANDARD);    // 加密方式
            parameters.setPassword(passwd.toCharArray());
        }
        try {
            ZipFile zipFile = new ZipFile(dest);
            if (srcFile.isDirectory()) {
                // 如果不創建目錄的話,將直接把給定目錄下的文件壓縮到壓縮文件,即沒有目錄結構
                if (!isCreateDir) {
                    File [] subFiles = srcFile.listFiles();
                    ArrayList<File> temp = new ArrayList<File>();
                    Collections.addAll(temp, subFiles);
                    zipFile.addFiles(temp, parameters);
                    return dest;
                }
                zipFile.addFolder(srcFile, parameters);
            } else {
                zipFile.addFile(srcFile, parameters);
            }
            return dest;
        } catch (ZipException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 構建壓縮文件存放路徑,如果不存在將會創建
     * 傳入的可能是文件名或者目錄,也可能不傳,此方法用以轉換最終壓縮文件的存放路徑
     * @param srcFile 源文件
     * @param destParam 壓縮目標路徑
     * @return 正確的壓縮文件存放路徑
     */
    private static String buildDestinationZipFilePath(File srcFile,String destParam) {
        if (StringUtils.isEmpty(destParam)) {
            if (srcFile.isDirectory()) {
                destParam = srcFile.getParent() + File.separator + srcFile.getName() + ".zip";
            } else {
                String fileName = srcFile.getName().substring(0, srcFile.getName().lastIndexOf("."));
                destParam = srcFile.getParent() + File.separator + fileName + ".zip";
            }
        } else {
            createDestDirectoryIfNecessary(destParam);    // 在指定路徑不存在的情況下將其創建出來
            if (destParam.endsWith(File.separator)) {
                String fileName = "";
                if (srcFile.isDirectory()) {
                    fileName = srcFile.getName();
                } else {
                    fileName = srcFile.getName().substring(0, srcFile.getName().lastIndexOf("."));
                }
                destParam += fileName + ".zip";
            }
        }
        return destParam;
    }
    
    /**
     * 在必要的情況下創建壓縮文件存放目錄,比如指定的存放路徑並沒有被創建
     * @param destParam 指定的存放路徑,有可能該路徑並沒有被創建
     */
    private static void createDestDirectoryIfNecessary(String destParam) {
        File destDir = null;
        if (destParam.endsWith(File.separator)) {
            destDir = new File(destParam);
        } else {
            destDir = new File(destParam.substring(0, destParam.lastIndexOf(File.separator)));
        }
        if (!destDir.exists()) {
            destDir.mkdirs();
        }
    }

    public static void main(String[] args) {
        zip("d:\\test\\cc", "d:\\test\\cc.zip", "11");
//        try {
//            File[] files = unzip("d:\\test\\漢字.zip", "aa");
//            for (int i = 0; i < files.length; i++) {
//                System.out.println(files[i]);
//            }
//        } catch (ZipException e) {
//            e.printStackTrace();
//        }
    }
}

需要學習的東西太多,沒太多時間(或許只是借口)去研究它,上面的例子僅是簡單地解壓和壓縮操作;但在使用中可以發現Zip4J功能比較完備,如果需要更多地支持,那就真要好好去研究一下它,也許它真的不會使您失望。。。

 

補充

 

刪除壓縮文件中的目錄

 

看到有朋友在問如何刪除壓縮文件中的目錄,在這里補充一下。

利用zip4j刪除壓縮文件中的目錄,查閱API后很容易想到這樣的方式:

ZipFile zipFile = new ZipFile("d:\\FeiQ-V2.5.zip");
zipFile.setFileNameCharset("GBK");
zipFile.removeFile("sounds/");        // sounds是zip文件中的一個目錄

但這種直接刪除壓縮文件中非空目錄的方式是不會成功的,你會看到zip文件絲毫沒有變化,雖然目錄對應的FileHeader已被刪除(表現就是如果這時再將目錄下的所有文件刪除,則該目錄隨之消失) ;因此我們需要將該目錄下所有的文件都刪除掉,最后再將目錄刪除,根據這個思路,我們很容易形成如下的代碼:

void removeDirFromZipArchive(String file, String removeDir) throws ZipException {
    // 創建ZipFile並設置編碼
    ZipFile zipFile = new ZipFile(file);
    zipFile.setFileNameCharset("GBK");
    
    // 給要刪除的目錄加上路徑分隔符
    if (!removeDir.endsWith(File.separator)) removeDir += File.separator;

    // 如果目錄不存在, 直接返回
    FileHeader dirHeader = zipFile.getFileHeader(removeDir);
    if (null == dirHeader) return;

    // 遍歷壓縮文件中所有的FileHeader, 將指定刪除目錄下的子文件刪除
    List allHeaders = zipFile.getFileHeaders();
    for(int i=0, len = allHeaders.size(); i<len; i++) {
        FileHeader subHeader = (FileHeader) allHeaders.get(i);
        if (subHeader.getFileName().startsWith(dirHeader.getFileName())
                && !subHeader.getFileName().equals(dirHeader.getFileName())) {
            zipFile.removeFile(subHeader);
        }
    }
    // 最后刪除指定目錄
    zipFile.removeFile(dirHeader);
}

這樣仍然解決不了問題,如果你這樣做了,那么你將會得到一個java.lang.IndexOutOfBoundsException異常,那么看似正常的代碼為什么會報索引越界異常呢?其實我們通過zipFile.getFileHeaders()方法得到的List會隨遍歷中的刪除操作而發生變化,也就是說我們刪除了某個FileHeader,將會反映到該List中。每成功刪除一個FileHeader,List長度就減1,而i一直在0至List的初始長度之間遞增,反復幾次后就可能出現越界異常。

為了避免這種情況發生,我們可以多做一些操作,比如可以在遍歷中暫不進行刪除操作,而只是將要刪除的文件記錄下來,遍歷結束后再統一刪除,最后將目錄刪除,經測試,這個思路可以解決問題。

簡單示例代碼:

void removeDirFromZipArchive(String file, String removeDir) throws ZipException {
    // 創建ZipFile並設置編碼
    ZipFile zipFile = new ZipFile(file);
    zipFile.setFileNameCharset("GBK");
    
    // 給要刪除的目錄加上路徑分隔符
    if (!removeDir.endsWith(File.separator)) removeDir += File.separator;
    
    // 如果目錄不存在, 直接返回
    FileHeader dirHeader = zipFile.getFileHeader(removeDir);
    if (null == dirHeader) return;

    // 遍歷壓縮文件中所有的FileHeader, 將指定刪除目錄下的子文件名保存起來
    List headersList = zipFile.getFileHeaders();
    List<String> removeHeaderNames = new ArrayList<String>();
    for(int i=0, len = headersList.size(); i<len; i++) {
        FileHeader subHeader = (FileHeader) headersList.get(i);
        if (subHeader.getFileName().startsWith(dirHeader.getFileName())
                && !subHeader.getFileName().equals(dirHeader.getFileName())) {
            removeHeaderNames.add(subHeader.getFileName());
        }
    }
    // 遍歷刪除指定目錄下的所有子文件, 最后刪除指定目錄(此時已為空目錄)
    for(String headerNameString : removeHeaderNames) {
        zipFile.removeFile(headerNameString);
    }
    zipFile.removeFile(dirHeader);
}

 

隨着這個Zip4j的庫的使用,之前的郁悶全部解開了

轉 : http://blog.csdn.net/zhyh1986/article/details/7921376  

注 : 還有一篇也不錯:  http://rensanning.iteye.com/blog/1836727


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM