1. 程式人生 > 其它 >把多個檔案打包壓縮成tar.gz檔案並解壓的Java實現

把多個檔案打包壓縮成tar.gz檔案並解壓的Java實現

介紹如何把多個檔案壓縮成gz檔案,並實現解壓。

壓縮檔案

  在Java中,可以 使用GZIPOutputStream建立gzip(gz)壓縮檔案,它在commons-compress下面,可以通過如下的maven座標引入:

<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-compress</artifactId>
	<version>1.21</version>
</dependency>

tar.gz檔案可以理解為通過如下方式獲取的檔案:先用tar打包,再使用gz進行壓縮。下面直接上程式碼:

package com.eg.wiener.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import org.apache.commons.lang3.RandomStringUtils;

import java.io.*;
import java.util.zip.GZIPOutputStream;

@Slf4j
public class FileUtils {

    /**
     *
     * 壓縮檔案
     *
     * @param sourceFolder 指定打包的源目錄
     * @param tarGzPath    指定目標 tar 包的位置
     */
    private static void compress(String sourceFolder, String tarGzPath) {
        log.info("壓縮後文件名:{}", tarGzPath);
        TarArchiveOutputStream tarOs = null;
        try {
            // 建立一個 FileOutputStream 到輸出檔案(.tar.gz)
            FileOutputStream fos = new FileOutputStream(tarGzPath);
            // 建立一個 GZIPOutputStream,用來包裝 FileOutputStream 物件
            GZIPOutputStream gos = new GZIPOutputStream(new BufferedOutputStream(fos));
            // 建立一個 TarArchiveOutputStream,用來包裝 GZIPOutputStream 物件
            tarOs = new TarArchiveOutputStream(gos);
            // 使檔名支援超過 100 個位元組
            tarOs.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
            File sourceFile = new File(sourceFolder);
            //遍歷源目錄的檔案,將所有檔案遷移到新的目錄tarGzPath下
            File[] sources = sourceFile.listFiles();
            for (File oneFile : sources) {
                addFilesToTarGZ(oneFile.getPath(), "", tarOs);
            }
        } catch (IOException e) {
            log.error("壓縮失敗,", e);
        } finally {
            try {
                tarOs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * @param sourcePath 原始檔
     * @param parent     源目錄
     * @param tarArchive 壓縮輸出流
     * @throws IOException
     */
    public static void addFilesToTarGZ(String sourcePath, String parent, TarArchiveOutputStream tarArchive) throws IOException {
        File sourceFile = new File(sourcePath);
        // 獲取新目錄下的檔名稱
        String fileName = parent.concat(sourceFile.getName());
        //打包壓縮該檔案
        tarArchive.putArchiveEntry(new TarArchiveEntry(sourceFile, fileName));
        if (sourceFile.isFile()) {
            FileInputStream fis = new FileInputStream(sourceFile);
            BufferedInputStream bis = new BufferedInputStream(fis);
            // 寫入檔案
            IOUtils.copy(bis, tarArchive);
            tarArchive.closeArchiveEntry();
            bis.close();
        } else if (sourceFile.isDirectory()) {
            // 因為是個資料夾,無需寫入內容,關閉即可
            tarArchive.closeArchiveEntry();
            // 遍歷資料夾下的檔案
            for (File f : sourceFile.listFiles()) {
                // 遞迴遍歷檔案目錄樹
                addFilesToTarGZ(f.getAbsolutePath(), fileName + File.separator, tarArchive);
            }
        }
    }

}

解壓檔案

  解壓檔案時依賴的jar包如下:

       <!--解壓-->
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.5</version>
        </dependency>

  壓縮檔案時遇到了繁瑣的關閉資源的問題,故這裡優先考慮使用try-with-resources,而不是try-finally。由此帶來的好處是使程式碼更簡潔、更清晰,即便是丟擲的異常,也更有價值,這些優點是try-finally無法做到的。

   /**
     * 解壓tar.gz 檔案
     * @param targzFile 要解壓的tar.gz檔案物件
     * @param outPath 要解壓到某個指定的目錄下
     * @throws IOException
     */
    public static void unpack(File targzFile, String outPath) {

        // 驗證引數
        if (targzFile == null || !targzFile.isFile() || StringUtils.isEmpty(outPath)) {
            log.error("檔案解壓縮執行異常,請檢查輸入引數!");
            return;
        }
        // 讀取 .tar.gz 檔案轉換為 tar 檔案
        try (FileInputStream is = new FileInputStream(targzFile);
             BufferedInputStream bis = new BufferedInputStream(is);
             GZIPInputStream gzipIs = new GZIPInputStream(bis);
             TarInputStream tarIs = new TarInputStream(gzipIs, 1024 * 2)) {
            // 迭代 tar 檔案集合,解壓檔案
            for (TarEntry entry = tarIs.getNextEntry(); entry != null; entry = tarIs.getNextEntry()) {
                File targetFileName = new File(outPath + "/" + entry.getName());
                IOUtils.copy(tarIs, new FileOutputStream(targetFileName));
            }
            log.info("檔案 {} 解壓完畢", targzFile);
        } catch (Exception e) {
            log.error("{} 解壓異常!", targzFile, e);
        }
    }

  測試用例就放在一個main函式裡了,如下所示:

    public static void main(String[] args) throws IOException {
        // 把F:\img\source內的檔案及其資料夾打包成名為資料夾F:\img\target下的、名字為六位隨機數的 gz 壓縮包
        String targetPath = "F:\\img\\target\\" + RandomStringUtils.randomAlphanumeric(6) + ".tar.gz";
        compress("F:\\img\\source", targetPath);
        log.info("=====done====");
        unpack(new File(targetPath) , "F:\\img\\unpack");
    }

小結

  小編樓蘭胡楊在此介紹瞭如何把多個檔案壓縮成gz檔案,並實現解壓。如果你有更好的方案,請留言。


  讀後有收穫,小禮物走一走,請作者喝咖啡。

讚賞支援