Apache Commons Compress 文件解壓縮庫

目錄

Apache Commons Compress 概述

本地文件解壓縮代碼示例

文件壓縮並提供網絡下載


Apache Commons Compress 概述

1、Apache Commons Compress 官網:http://commons.apache.org/proper/commons-compress/index.html

2、Apache Commons Compress 庫定義了一個用於處理 ar,cpio,Unix 轉儲,tar,zip,gzip,XZ,Pack200,bzip2、7z,arj,lzma,snappy,DEFLATE,lz4,Brotli,Zstandard,DEFLATE64 和 Z 文件的 API 。

3、當前 Compress 版本是 1.19,並且需要 Java 7 及以上支持。

現在 ParallelScatterZipCreator 會按照條目添加到存檔的順序來寫入條目。

現在默認情況下解析額外的字段時 ZipArchiveInputStream和ZipFile 更具寬容性。

TarArchiveInputStream 具有新的寬鬆模式,該模式可能允許它讀取某些已損壞的檔案。

4、官網用戶使用手冊提供了各種壓縮格式的處理方式(本文僅以常用的 zip 格式爲例進行介紹):http://commons.apache.org/proper/commons-compress/examples.html

5、Apache Commons Compress 官網下載:http://commons.apache.org/proper/commons-compress/download_compress.cgi

6、可以從 Maven 中央倉庫獲取依賴:https://mvnrepository.com/artifact/org.apache.commons/commons-compress

<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.19</version>
</dependency>

核心 API

1)壓縮輸入流,用於解壓壓縮文件:public abstract class ArchiveInputStream extends java.io.InputStream

2)壓縮輸處出流,用於壓縮壓縮文件:public abstract class ArchiveOutputStream extends java.io.OutputStream

3)壓縮文件內部存檔的條目,壓縮文件內部的每一個被壓縮文件都稱爲一個條目:public interface ArchiveEntry

本地文件解壓縮代碼示例

1、生產中常見的需求之一就是對服務器上的某些文件進行壓縮,或者解壓。本文以常用的 zip 格式爲例進行介紹。

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import java.io.*;
/**
 * 壓縮工具類
 *
 * @author helloworld
 */
public class ZipUtil {

    /**
     * 將文件打包成 zip 壓縮包文件
     *
     * @param sourceFiles        待壓縮的多個文件列表。只支持文件,不能有目錄,否則拋異常。
     * @param zipFile            壓縮文件。文件可以不存在,但是目錄必須存在,否則拋異常。如 C:\Users\Think\Desktop\aa.zip
     * @param isDeleteSourceFile 是否刪除源文件(sourceFiles)
     * @return 是否壓縮成功
     */
    public static boolean archiveFiles2Zip(File[] sourceFiles, File zipFile, boolean isDeleteSourceFile) {
        InputStream inputStream = null;//源文件輸入流
        ZipArchiveOutputStream zipArchiveOutputStream = null;//壓縮文件輸出流
        if (sourceFiles == null || sourceFiles.length <= 0) {
            return false;
        }
        try {
            zipArchiveOutputStream = new ZipArchiveOutputStream(zipFile);//ZipArchiveOutputStream(File file) :根據文件構建壓縮輸出流,將源文件壓縮到此文件.
            //setUseZip64(final Zip64Mode mode):是否使用 Zip64 擴展。
            // Zip64Mode 枚舉有 3 個值:Always:對所有條目使用 Zip64 擴展、Never:不對任何條目使用Zip64擴展、AsNeeded:對需要的所有條目使用Zip64擴展
            zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);
            for (File file : sourceFiles) {
                //將每個源文件用 ZipArchiveEntry 實體封裝,然後添加到壓縮文件中. 這樣將來解壓后里面的文件名稱還是保持一致.
                ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(file.getName());
                zipArchiveOutputStream.putArchiveEntry(zipArchiveEntry);
                inputStream = new FileInputStream(file);//獲取源文件輸入流
                byte[] buffer = new byte[1024 * 5];
                int length = -1;//每次讀取的字節大小。
                while ((length = inputStream.read(buffer)) != -1) {
                    //把緩衝區的字節寫入到 ZipArchiveEntry
                    zipArchiveOutputStream.write(buffer, 0, length);
                }
            }
            zipArchiveOutputStream.closeArchiveEntry();//寫入此條目的所有必要數據。如果條目未壓縮或壓縮後的大小超過4 GB 則拋出異常
            zipArchiveOutputStream.finish();//壓縮結束.
            if (isDeleteSourceFile) {//爲 true 則刪除源文件.
                for (File file : sourceFiles) {
                    file.deleteOnExit();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            //關閉輸入、輸出流,釋放資源.
            try {
                if (null != inputStream) {
                    inputStream.close();
                }
                if (null != zipArchiveOutputStream) {
                    zipArchiveOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    /**
     * 將 zip 壓縮包解壓成文件到指定文件夾下
     *
     * @param zipFile   待解壓的壓縮文件。親測  .zip 文件有效;.7z 壓縮解壓時拋異常。
     * @param targetDir 解壓後文件存放的目的地. 此目錄必須存在,否則異常。
     * @return 是否成功
     */
    public static boolean decompressZip2Files(File zipFile, File targetDir) {
        InputStream inputStream = null;//源文件輸入流,用於構建 ZipArchiveInputStream
        OutputStream outputStream = null;//解壓縮的文件輸出流
        ZipArchiveInputStream zipArchiveInputStream = null;//zip 文件輸入流
        ArchiveEntry archiveEntry = null;//壓縮文件實體.
        try {
            inputStream = new FileInputStream(zipFile);//創建輸入流,然後轉壓縮文件輸入流
            zipArchiveInputStream = new ZipArchiveInputStream(inputStream, "UTF-8");
            //遍歷解壓每一個文件.
            while (null != (archiveEntry = zipArchiveInputStream.getNextEntry())) {
                String archiveEntryFileName = archiveEntry.getName();//獲取文件名
                File entryFile = new File(targetDir, archiveEntryFileName);//把解壓出來的文件寫到指定路徑
                byte[] buffer = new byte[1024 * 5];
                outputStream = new FileOutputStream(entryFile);
                int length = -1;
                while ((length = zipArchiveInputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, length);
                }
                outputStream.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (null != outputStream) {
                    outputStream.close();
                }
                if (null != zipArchiveInputStream) {
                    zipArchiveInputStream.close();
                }
                if (null != inputStream) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }
}

文件壓縮並提供網絡下載

1、生產中另一個常見的需求是,用戶下載的時候,有時候需要將多個文件打包成一個文件後提供下載。

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 壓縮文件下載工具類。用於將多文件文件壓縮後,提供給 ServletOutputStream 輸出流共用戶網頁下載
 */
@SuppressWarnings("all")
public class ZipFileDownloadUtils {

    /**
     * 壓縮本地多個文件並提供輸出流下載
     *
     * @param filePaths   :本地文件路徑,如 ["C:\\wmx\\temp\\data1.json","C:\\wmx\\temp\\data2.json"]。只支持文件,不能有目錄,否則拋異常。
     * @param zipFileName :壓縮文件輸出的名稱,如 "年終總結" 不含擴展名。默認文件當前時間。如 20200108111213.zip
     * @param response    :提供輸出流
     */
    public static void zipFileDownloadByFile(Set<String> filePaths, String zipFileName, HttpServletResponse response) {
        try {
            //1)參數校驗
            if (filePaths == null || filePaths.size() <= 0) {
                throw new RuntimeException("待壓縮導出文件爲空.");
            }
            if (zipFileName == null || "".equals(zipFileName)) {
                zipFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip";
            } else {
                zipFileName = zipFileName + ".zip";
            }
            //2)設置 response 參數。這裏文件名如果是中文,則導出亂碼,可以
            response.reset();
            response.setContentType("content-type:octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFileName, "utf-8"));

            //3)通過 OutputStream 創建 zip 壓縮流。如果是壓縮到本地,也可以直接使用 ZipArchiveOutputStream(final File file)
            ServletOutputStream servletOutputStream = response.getOutputStream();
            ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(servletOutputStream);
            //4)setUseZip64(final Zip64Mode mode):是否使用 Zip64 擴展。
            // Zip64Mode 枚舉有 3 個值:Always:對所有條目使用 Zip64 擴展、Never:不對任何條目使用Zip64擴展、AsNeeded:對需要的所有條目使用Zip64擴展
            zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);

            for (String filePath : filePaths) {
                File file = new File(filePath);
                String fileName = file.getName();
                InputStream inputStream = new FileInputStream(file);
                //5)使用 ByteArrayOutputStream 讀取文件字節
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int readLength = -1;
                while ((readLength = inputStream.read(buffer)) != -1) {
                    byteArrayOutputStream.write(buffer, 0, readLength);
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                }
                byte[] fileBytes = byteArrayOutputStream.toByteArray();//整個文件字節數據

                //6)用指定的名稱創建一個新的 zip 條目(zip壓縮實體),然後設置到 zip 壓縮輸出流中進行寫入.
                ArchiveEntry entry = new ZipArchiveEntry(fileName);
                zipArchiveOutputStream.putArchiveEntry(entry);
                //6.1、write(byte b[]):從指定的字節數組寫入 b.length 個字節到此輸出流
                zipArchiveOutputStream.write(fileBytes);
                //6.2、寫入此條目的所有必要數據。如果條目未壓縮或壓縮後的大小超過4 GB 則拋出異常
                zipArchiveOutputStream.closeArchiveEntry();
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            }
            //7)最後關閉 zip 壓縮輸出流.
            if (zipArchiveOutputStream != null) {
                zipArchiveOutputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 壓縮本地多個文件並提供輸出流下載。只支持文件,不能有目錄,否則拋異常。
     *
     * @param fileLists   :本地文件,如 ["C:\\wmx\\temp\\data1.json","C:\\wmx\\temp\\data2.json"]。
     * @param zipFileName :壓縮文件輸出的名稱,如 "年終總結" 不含擴展名。默認文件當前時間。如 20200108111213.zip
     * @param response    :提供輸出流
     */
    public static void zipFileDownloadByFile(List<File> fileLists, String zipFileName, HttpServletResponse response) {
        if (fileLists == null || fileLists.size() <= 0) {
            throw new RuntimeException("待壓縮導出文件爲空.");
        }
        Set<String> filePaths = new HashSet<>();
        for (File file : fileLists) {
            filePaths.add(file.getAbsolutePath());
        }
        zipFileDownloadByFile(filePaths, zipFileName, response);
    }

    /**
     * 壓縮網絡文件。
     *
     * @param urlLists,待壓縮的網絡文件地址,如 ["http://www.baidu.com/img/bd_logo1.png"]
     * @param zipFileName
     * @param response
     */
    public static void zipFileDownloadByUrl(List<URL> urlLists, String zipFileName, HttpServletResponse response) {
        try {
            //1)參數校驗
            if (urlLists == null || urlLists.size() <= 0) {
                throw new RuntimeException("待壓縮導出文件爲空.");
            }
            if (zipFileName == null || "".equals(zipFileName)) {
                zipFileName = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ".zip";
            } else {
                zipFileName = zipFileName + ".zip";
            }
            //2)設置 response 參數
            response.reset();
            response.setContentType("content-type:octet-stream;charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(zipFileName, "utf-8"));

            //3)通過 OutputStream 創建 zip 壓縮流。如果是壓縮到本地,也可以直接使用 ZipArchiveOutputStream(final File file)
            ServletOutputStream servletOutputStream = response.getOutputStream();
            ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(servletOutputStream);
            //4)setUseZip64(final Zip64Mode mode):是否使用 Zip64 擴展。
            // Zip64Mode 枚舉有 3 個值:Always:對所有條目使用 Zip64 擴展、Never:不對任何條目使用Zip64擴展、AsNeeded:對需要的所有條目使用Zip64擴展
            zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);

            for (URL url : urlLists) {
                String fileName = getNameByUrl(url);
                InputStream inputStream = url.openStream();
                //5)使用 ByteArrayOutputStream 讀取文件字節
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                byte[] buffer = new byte[1024];
                int readLength = -1;
                while ((readLength = inputStream.read(buffer)) != -1) {
                    byteArrayOutputStream.write(buffer, 0, readLength);
                }
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.flush();
                }
                byte[] fileBytes = byteArrayOutputStream.toByteArray();//整個文件字節數據

                //6)用指定的名稱創建一個新的 zip 條目(zip壓縮實體),然後設置到 zip 壓縮輸出流中進行寫入.
                ArchiveEntry entry = new ZipArchiveEntry(fileName);
                zipArchiveOutputStream.putArchiveEntry(entry);
                //6.1、write(byte b[]):從指定的字節數組寫入 b.length 個字節到此輸出流
                zipArchiveOutputStream.write(fileBytes);
                //6.2、寫入此條目的所有必要數據。如果條目未壓縮或壓縮後的大小超過4 GB 則拋出異常
                zipArchiveOutputStream.closeArchiveEntry();
                if (byteArrayOutputStream != null) {
                    byteArrayOutputStream.close();
                }
            }
            //7)最後關閉 zip 壓縮輸出流.
            if (zipArchiveOutputStream != null) {
                zipArchiveOutputStream.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 壓縮網絡文件
     *
     * @param urlPaths    待壓縮的網絡文件地址,如 ["http://www.baidu.com/img/bd_logo1.png"]
     * @param zipFileName
     * @param response
     */
    public static void zipFileDownloadByUrl(Set<String> urlPaths, String zipFileName, HttpServletResponse response) {
        if (urlPaths == null || urlPaths.size() <= 0) {
            throw new RuntimeException("待壓縮導出文件爲空.");
        }
        List<URL> urlList = new ArrayList<>();
        for (String urlPath : urlPaths) {
            try {
                urlList.add(new URL(urlPath));
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
        }
        zipFileDownloadByUrl(urlList, zipFileName, response);
    }

    /**
     * 通過 url 獲取文件的名稱
     *
     * @param url,如 http://www.baidu.com/img/bd_logo1.png
     * @return
     */
    private static String getNameByUrl(URL url) {
        String name = url.toString();
        int lastIndexOf1 = name.lastIndexOf("/");
        int lastIndexOf2 = name.lastIndexOf("\\");
        if (lastIndexOf1 > 0) {
            name = name.substring(lastIndexOf1 + 1, name.length());
        } else if (lastIndexOf2 > 0) {
            name = name.substring(lastIndexOf1 + 2, name.length());
        }
        return name;
    }
}

 

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