Java GZip 基於磁碟實現壓縮和解壓的方法
阿新 • • 發佈:2020-08-24
GZip是常用的無失真壓縮演算法實現,在Linux中較為常見,像我們在Linux安裝軟體時,基本都是.tar.gz格式。.tar.gz格式檔案需要先對目錄內檔案進行tar壓縮,然後使用GZip進行壓縮。
本文針對基於磁碟的壓縮和解壓進行演示,演示只針對一層目錄結構進行,多層目錄只需遞迴操作進行即可。
Maven依賴
org.apache.commons: commons-compress: 1.19: 此依賴封裝了很多壓縮演算法相關的工具類,提供的API還是相對比較底層,我們今天在它的基礎上做進一步封裝。
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.19</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
工具類
其實,在通常情況下,我們都是在磁碟上進行壓縮和解壓操作的,這樣雖然增加了操作的複雜度,但是卻無形中避免了一些問題。
工具類針對.tar.gz格式提供了compressByTar、decompressByTar、compressByGZip、decompressByGZip四個方法,用於處理.tar.gz格式壓縮檔案,程式碼如下:
package com.arhorchin.securitit.compress.gzip; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; import org.apache.commons.compress.utils.IOUtils; import org.apache.log4j.Logger; /** * @author Securitit. * @note 基於磁碟以GZIP演算法進行壓縮和解壓工具類. */ public class GZipDiskUtil { /** * logger. */ private static Logger logger = Logger.getLogger(GZipDiskUtil.class); /** * UTF-8字符集. */ public static String CHARSET_UTF8 = "UTF-8"; /** * 使用TAR演算法進行壓縮. * @param sourceFolderPath 待進行壓縮的資料夾路徑. * @param targetTarFilePath 壓縮後的TAR檔案儲存目錄. * @return 壓縮是否成功. * @throws Exception 壓縮過程中可能發生的異常. */ public static boolean compressByTar(String sourceFolderPath,String targetTarFilePath) throws Exception { // 變數定義. File sourceFolderFile = null; FileOutputStream targetTarFos = null; TarArchiveOutputStream targetTartTaos = null; TarArchiveEntry targetTarTae = null; try { // 壓縮變數初始化. sourceFolderFile = new File(sourceFolderPath); targetTarFos = new FileOutputStream(new File(targetTarFilePath)); targetTartTaos = new TarArchiveOutputStream(targetTarFos); // 將檔案新增到ZIP條目中. for (File file : sourceFolderFile.listFiles()) { try (FileInputStream fis = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(fis);) { targetTarTae = new TarArchiveEntry(file); targetTarTae.setName(file.getName()); targetTartTaos.putArchiveEntry(targetTarTae); targetTartTaos.write(IOUtils.toByteArray(bis)); targetTartTaos.closeArchiveEntry(); } } } catch (Exception ex) { logger.info("GZipDiskUtil.compressByTar.",ex); return false; } finally { if (targetTartTaos != null) targetTartTaos.close(); if (targetTarFos != null) targetTarFos.close(); } return true; } /** * 使用TAR演算法進行解壓. * @param sourceTarPath 待解壓檔案路徑. * @param targetFolderPath 解壓後文件夾目錄. * @return 解壓是否成功. * @throws Exception 解壓過程中可能發生的異常. */ public static boolean decompressByTar(String sourceTarPath,String targetFolderPath) throws Exception { // 變數定義. FileInputStream sourceTarFis = null; TarArchiveInputStream sourceTarTais = null; TarArchiveEntry sourceTarTae = null; File singleEntryFile = null; try { // 解壓定義初始化. sourceTarFis = new FileInputStream(new File(sourceTarPath)); sourceTarTais = new TarArchiveInputStream(sourceTarFis); // 條目解壓縮至指定資料夾目錄下. while ((sourceTarTae = sourceTarTais.getNextTarEntry()) != null) { singleEntryFile = new File(targetFolderPath + File.separator + sourceTarTae.getName()); try (FileOutputStream fos = new FileOutputStream(singleEntryFile); BufferedOutputStream bos = new BufferedOutputStream(fos);) { bos.write(IOUtils.toByteArray(sourceTarTais)); } } } catch (Exception ex) { logger.info("GZipDiskUtil.decompressByTar.",ex); return false; } finally { if (sourceTarTais != null) sourceTarTais.close(); if (sourceTarFis != null) sourceTarFis.close(); } return true; } /** * 使用GZIP演算法進行壓縮. * @param sourceFilePath 待進行壓縮的檔案路徑. * @param targetGZipFilePath 壓縮後的GZIP檔案儲存目錄. * @return 壓縮是否成功. * @throws Exception 壓縮過程中可能發生的異常. */ public static boolean compressByGZip(String sourceFilePath,String targetGZipFilePath) throws IOException { // 變數定義. FileInputStream sourceFileFis = null; BufferedInputStream sourceFileBis = null; FileOutputStream targetGZipFileFos = null; BufferedOutputStream targetGZipFileBos = null; GzipCompressorOutputStream targetGZipFileGcos = null; try { // 壓縮變數初始化. sourceFileFis = new FileInputStream(new File(sourceFilePath)); sourceFileBis = new BufferedInputStream(sourceFileFis); targetGZipFileFos = new FileOutputStream(targetGZipFilePath); targetGZipFileBos = new BufferedOutputStream(targetGZipFileFos); targetGZipFileGcos = new GzipCompressorOutputStream(targetGZipFileBos); // 採用commons-compress提供的方式進行壓縮. targetGZipFileGcos.write(IOUtils.toByteArray(sourceFileBis)); } catch (Exception ex) { logger.info("GZipDiskUtil.compressByGZip.",ex); return false; } finally { if (targetGZipFileGcos != null) targetGZipFileGcos.close(); if (targetGZipFileBos != null) targetGZipFileBos.close(); if (targetGZipFileFos != null) targetGZipFileFos.close(); if (sourceFileBis != null) sourceFileBis.close(); if (sourceFileFis != null) sourceFileFis.close(); } return true; } /** * 使用GZIP演算法進行解壓. * @param sourceGZipFilePath 待解壓檔案路徑. * @param targetFilePath 解壓後文件路徑. * @return 解壓是否成功. * @throws @throws Exception 解壓過程中可能發生的異常. */ public static boolean decompressByGZip(String sourceGZipFilePath,String targetFilePath) throws IOException { // 變數定義. FileInputStream sourceGZipFileFis = null; BufferedInputStream sourceGZipFileBis = null; FileOutputStream targetFileFos = null; GzipCompressorInputStream sourceGZipFileGcis = null; try { // 解壓變數初始化. sourceGZipFileFis = new FileInputStream(new File(sourceGZipFilePath)); sourceGZipFileBis = new BufferedInputStream(sourceGZipFileFis); sourceGZipFileGcis = new GzipCompressorInputStream(sourceGZipFileBis); targetFileFos = new FileOutputStream(new File(targetFilePath)); // 採用commons-compress提供的方式進行解壓. targetFileFos.write(IOUtils.toByteArray(sourceGZipFileGcis)); } catch (Exception ex) { logger.info("GZipDiskUtil.decompressByGZip.",ex); return false; } finally { if (sourceGZipFileGcis != null) sourceGZipFileGcis.close(); if (sourceGZipFileBis != null) sourceGZipFileBis.close(); if (sourceGZipFileFis != null) sourceGZipFileFis.close(); if (targetFileFos != null) targetFileFos.close(); } return true; } }
工具類測試
在Maven依賴引入正確的情況下,複製上面的程式碼到專案中,修改package,可以直接使用,下面我們對工具類進行簡單測試。測試類程式碼如下:
package com.arhorchin.securitit.compress.gzip; import com.arhorchin.securitit.compress.gzip.GZipDiskUtil; /** * @author Securitit. * @note GZipDiskUtil工具類測試. */ public class GZipDiskUtilTester { public static void main(String[] args) throws Exception { GZipDiskUtil.compressByTar("C:/Users/Administrator/Downloads/個人檔案/2020-07-13/files","C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk.tar"); GZipDiskUtil.compressByGZip("C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk.tar","C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk.tar.gz"); GZipDiskUtil.decompressByGZip("C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk.tar.gz","C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk-untar.tar"); GZipDiskUtil.decompressByTar("C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk-untar.tar","C:/Users/Administrator/Downloads/個人檔案/2020-07-13/disk-untar"); } }
執行測試後,通過檢視disk.tar、disk.tar.gz、disk-untar.tar和解壓的目錄,可以確認工具類執行結果無誤。
總結
1) 在小檔案、檔案數量較小且較為固定時,提倡使用記憶體壓縮和解壓方式。使用記憶體換時間,減少頻繁的磁碟操作。《Java GZip 基於記憶體實現壓縮和解壓》
2) 在大檔案、檔案數量較大時,提倡使用磁碟壓縮和解壓方式。過大檔案對服務會造成過度的負載,磁碟壓縮和解壓可以緩解這種壓力。