壓縮文件
在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文件,並實現解壓。如果你有更好的方案,請留言。