1. 程式人生 > 程式設計 >Java GZip 基於磁碟實現壓縮和解壓的方法

Java GZip 基於磁碟實現壓縮和解壓的方法

  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) 在大檔案、檔案數量較大時,提倡使用磁碟壓縮和解壓方式。過大檔案對服務會造成過度的負載,磁碟壓縮和解壓可以緩解這種壓力。