Java zip壓縮

Java中有與zip相關的api,位於java.util.zip.*包下。
在一些應用場景中,可能需要對程序生成的文件如導出比較大的excel文件,然後需要對其壓縮上傳。
《Java 核心技術 卷二 高級特性》這本書中就有關於zip的介紹,下面將其摘錄如下:

1.讀取zip文件

zip文檔通常以壓縮格式存儲了一個或多個文件,每個zip文檔都有一個包含諸如文件名字和所使用的壓縮方法等信息的頭。在Java中,可以使用ZipInputStream來讀入zip文檔。
你可能需要瀏覽文檔中每個單獨的項,getNextEntry方法就可以返回一個描述這些項的ZipEntry類型的對象。ZipInputStream的read方法被修改爲在碰到當前項的結尾時返回-1(而不是碰到zip文件的末尾),然後你必須調用closeEntry來讀入下一項。下面是典型的通讀zip文件的僞代碼:

ZipInputStream zin = new ZipInputStream(new FileInputStream(zipname));
ZipEntry entry;
while((entry=zin.getNextEntry())!=null)
{
    analyze entry;
    read the contents of zin;
    zin.closeEntry();
}
zin.close();

當希望讀入某個zip項的內容時,我們可能並不想使用原生的read方法,通常,我們將使用某個更能勝任的流過濾器的方法。例如,爲了讀入zip文件內部的一個文本文件,我們可以使用下面的循環:

Scanner in = new Scanner(zin);
while(in.hasNextLine())
    do something with in.nextLine();

注意: zip輸入流在讀入zip文件發生錯誤時,會拋出ZipException。通常這種錯誤在zip文件被破壞時發生。

2.寫出到zip文件

要寫出到zip文件,可以使用ZipOutputStream,而對於你希望放入到zip文件中的每一項,都應該創建一個ZipEntry對象,並將文件名傳遞給ZipEntry的構造器,它將設置其他諸如文件日期和解壓縮方法等參數。如果需要,你可以覆蓋這些設置。然後,你需要調用ZipOutputStream的putNextEntry方法來開始寫出新文件,並將文件數據發送到zip流中。當完成時,需要調用closeEntry。然後,你需要對所有你希望存儲的文件都重複這個過程。下面是代碼框架:

FileOutputStream fout = new FileOutputStream("test.zip");
ZipOutputStream zout = new ZipOutputStream(fout);
for all files
{
    ZipEntry ze = new ZipEntry(filename);//  file.getName();
    zout.putNextEntry(ze);
    send data to zout;
    zout.closeEntry();
}
zout.close();

注意: ZipEntry ze = new ZipEntry(filename);中的filename,這個路徑是相對路徑(相對於test.zip這個zip文件的路徑),所以如果是目錄層級的話,需要加上層級目錄。

上面這段僞代碼非常重要,可以幫助你很好的理解下面的遞歸結構程序。

package test;

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

public class ZipUtils {
    public static final String EXT = ".zip";  
    // 符號"/"用來作爲目錄標識判斷符  
    private static final String PATH = "/";  
    private static final int BUFFER = 1024;  

    /**
     * 壓縮文件或文件夾
     * @param zipOutfile zip輸出文件
     * @param file 待壓縮的文件或文件夾
     * @throws IOException
     */
    public static void compressFile(File zipOutfile,File file) throws IOException{
        System.out.println("Begin Compress file ...");
        String zipname = zipOutfile.getName();
        File outfile = new File(zipOutfile.getParent(),zipname.substring(0, zipname.lastIndexOf("."))+EXT);
        if(outfile.exists()) return;
        ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(outfile));
        putEntry(zout,file,PATH);
        zout.close();
        System.out.println("END");
    }
    /**
     * 遞歸地將 文件或目錄 放置到輸出流中 
     * @param zout
     * @param file 
     * @param curdir 當前目錄層級
     * @throws IOException
     */
    private static void putEntry(ZipOutputStream zout,File file,String curdir) throws IOException{
        if(file.isFile()){
            ZipEntry ze = new ZipEntry(curdir+file.getName());
            zout.putNextEntry(ze);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(  
                    file));  
            byte[] b = new byte[BUFFER];
            int count ;
            while((count=bis.read(b, 0, BUFFER))!=-1)
                zout.write(b, 0, count);
            bis.close();
            zout.closeEntry();
        }else{
            ZipEntry ze = new ZipEntry(curdir+file.getName()+PATH);//放置目錄到zout  帶 / 表示目錄
            zout.putNextEntry(ze);
            zout.closeEntry();
            for(File f : file.listFiles()) 
                putEntry(zout,f,curdir+file.getName()+PATH);
        }
    }
    /**
     * 壓縮文件或目錄 實際調用的是compressFile(ZipOutputStream,File)
     * @param file 文件或目錄
     * @throws IOException
     */
    public static void compressFile(File file)throws IOException{
        System.out.println("Begin Compress single file or dir...");
        String zipname = file.getName();
        System.out.println(file.getName()+","+file.getParent()+","+file.getPath());
        if(file.isFile()){
            File zipOutfile = new File(file.getParent(),zipname.substring(0, zipname.lastIndexOf("."))+EXT);
            compressFile(zipOutfile,file);
        }else{
            File zipOutfile = new File(file.getParent(),zipname+EXT);
            compressFile(zipOutfile,file);
        }
    }

    public static void main(String[] args) throws Exception {
        //File file = new File("E:\\CSV\\f.txt");
        File file = new File("E:/CSV/fd");
        compressFile(file);
    }
}

在壓縮某個文件夾時,比較費解的是如何將該文件夾的文件都放置在一個zip中,並且文件的層級關係保持不變,所以上面遞歸的核心地方是當子文件是文件夾時,需要遞歸地將該子文件夾中的文件entry放置到該文件夾下,所以參數需要傳遞一個表示當前層級的curdir。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章