目录
- 一.压缩为zip
- 1.压缩多个文件
- 2.压缩目录
- 二.zip解压缩
- 三.压缩为tar.gz
- 1.压缩多个文件
- 2.压缩目录
- 四.tar.gz解压缩
一.压缩为zip
使用Java提供的核心库,
java.util.zip
1.压缩多个文件
public class ZipMultipleFiles {
public static void main(String[] args) throws IOException {
// 文件所在的路径
List<String> srcFiles = Arrays.asList(
"src/main/resources/test1.txt",
"src/main/resources/test2.txt"
);
// 构造压缩文件对象
FileOutputStream fos = new FileOutputStream("src/main/resources/multiCompressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
// 向压缩包对象中添加多个文件
for (String srcFile : srcFiles) {
File fileToZip = new File(srcFile);
ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
zipOut.putNextEntry(zipEntry);
FileInputStream fis = new FileInputStream(fileToZip);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) >= 0) {
// 将文件信息写入压缩文件中
zipOut.write(bytes, 0, length);
}
fis.close();
}
zipOut.close();
fos.close();
}
}
2.压缩目录
- 要压缩子目录及其子目录文件,所以需要递归遍历
- 每次遍历找到的是目录时,我们都将其名称附加“/”,并将其以
ZipEntry
保存到压缩包中,从而保持压缩的目录结构。 - 每次遍历找到的是文件时,将其以字节码形式压缩到压缩包里面
public class ZipDirectory {
public static void main(String[] args) throws IOException, FileNotFoundException {
// 需要被压缩的文件夹
String sourceFile = "src/main/resources/zipTest";
// 文件夹压缩之后的文件夹对象
FileOutputStream fos = new FileOutputStream("src/main/resources/dirCompressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
// 递归压缩文件夹
File fileToZip = new File(sourceFile);
zipFile(fileToZip, fileToZip.getName(), zipOut);
// 关闭输出流
zipOut.close();
fos.close();
}
/**
* 将fileToZip文件夹及其子目录文件递归压缩到zip文件中
* @param fileToZip 递归当前处理对象,可能是文件夹,也可能是文件
* @param fileName fileToZip文件或文件夹名称
* @param zipOut 压缩文件输出流
* @throws IOException
*/
private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
// 不压缩隐藏文件夹
if (fileToZip.isHidden()) {
return;
}
// 判断压缩对象如果是一个文件夹
if (fileToZip.isDirectory()) {
if (fileName.endsWith("/")) {
// 如果文件夹是以“/”结尾,将文件夹作为压缩箱放入zipOut压缩输出流
zipOut.putNextEntry(new ZipEntry(fileName));
zipOut.closeEntry();
} else {
// 如果文件夹不是以“/”结尾,将文件夹结尾加上“/”之后作为压缩箱放入zipOut压缩输出流
zipOut.putNextEntry(new ZipEntry(fileName + "/"));
zipOut.closeEntry();
}
// 遍历文件夹子目录,进行递归的zipFile
File[] children = fileToZip.listFiles();
for (File childFile : children) {
zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
}
//如果当前递归对象是文件夹,加入ZipEntry之后就返回
return;
}
// 如果当前的fileToZip不是一个文件夹,是一个文件,将其以字节码形式压缩到压缩包里面
FileInputStream fis = new FileInputStream(fileToZip);
ZipEntry zipEntry = new ZipEntry(fileName);
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
// 关闭流
fis.close();
}
}
二.zip解压缩
public class UnzipFile {
public static void main(String[] args) throws IOException {
// 需要被解压的压缩文件
String fileZip = "src/main/resources/unzipTest/compressed.zip";
// 解压的目标目录
File destDir = new File("src/main/resources/unzipTest");
byte[] buffer = new byte[1024];
ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip));
// 获取压缩包中的entry,并将其解压
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
File newFile = newFile(destDir, zipEntry);
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
//解压完成一个entry,再解压下一个
zipEntry = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
}
// 在解压目标文件夹,新建一个文件
public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
File destFile = new File(destinationDir, zipEntry.getName());
String destDirPath = destinationDir.getCanonicalPath();
String destFilePath = destFile.getCanonicalPath();
if (!destFilePath.startsWith(destDirPath + File.separator)) {
throw new IOException("该解压项在目标文件夹之外: " + zipEntry.getName());
}
return destFile;
}
}
三.压缩为tar.gz
java中没有一种官方的API可以去创建tar.gz
文件。所以我们需要使用到第三方库Apache Commons Compress去创建.tar.gz
文件。
- tar文件准确的说是打包文件,将文件打包到一个tar文件中,文件名后缀是
.tar
- Gzip是将文件的存储空间压缩保存,文件名后缀是
.gz
-
tar.gz
或.tgz
通常是指将文件打包到一个tar文件中,并将它使用Gzip进行压缩。
maven坐标
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.20</version>
</dependency>
1.压缩多个文件
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.junit.jupiter.api.Test;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class TarGzTest {
@Test
void testFilesTarGzip() throws IOException {
// 输入文件,被压缩文件
Path path1 = Paths.get("/home/test/file-a.xml");
Path path2 = Paths.get("/home/test/file-b.txt");
List<Path> paths = Arrays.asList(path1, path2);
//输出文件压缩结果
Path output = Paths.get("/home/test/output.tar.gz");
// OutputStream输出流、BufferedOutputStream缓冲输出流
// GzipCompressorOutputStream是gzip压缩输出流
// TarArchiveOutputStream打tar包输出流(包含gzip压缩输出流)
try (OutputStream fOut = Files.newOutputStream(output);
BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {
// 遍历文件list
for (Path path : paths) {
// 该文件不是目录或者符号链接
if (!Files.isRegularFile(path)) {
throw new IOException("Support only file!");
}
// 将该文件放入tar包,并执行gzip压缩
TarArchiveEntry tarEntry = new TarArchiveEntry(
path.toFile(),
path.getFileName().toString());
tOut.putArchiveEntry(tarEntry);
Files.copy(path, tOut);
tOut.closeArchiveEntry();
}
// for循环完成之后,finish-tar包输出流
tOut.finish();
}
}
}
2.压缩目录
@Test
void testDirTarGzip() throws IOException {
// 被压缩打包的文件夹
Path source = Paths.get("/home/test");
// 如果不是文件夹抛出异常
if (!Files.isDirectory(source)) {
throw new IOException("请指定一个文件夹");
}
// 压缩之后的输出文件名称
String tarFileName = "/home/" + source.getFileName().toString() + ".tar.gz";
// OutputStream输出流、BufferedOutputStream缓冲输出流
// GzipCompressorOutputStream是gzip压缩输出流
// TarArchiveOutputStream打tar包输出流(包含gzip压缩输出流)
try (OutputStream fOut = Files.newOutputStream(Paths.get(tarFileName));
BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {
// 遍历文件目录树
Files.walkFileTree(source, new SimpleFileVisitor<Path>() {
//当成功访问到一个文件
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) throws IOException {
// 判断当前遍历文件是不是符号链接(快捷方式),不做打包压缩处理
if (attributes.isSymbolicLink()) {
return FileVisitResult.CONTINUE;
}
// 获取当前遍历文件名称
Path targetFile = source.relativize(file);
// 将该文件打包压缩
TarArchiveEntry tarEntry = new TarArchiveEntry(
file.toFile(), targetFile.toString());
tOut.putArchiveEntry(tarEntry);
Files.copy(file, tOut);
tOut.closeArchiveEntry();
// 继续下一个遍历文件处理
return FileVisitResult.CONTINUE;
}
// 当前遍历文件访问失败
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.printf("无法对该文件压缩打包为tar.gz : %s%n%s%n", file, exc);
return FileVisitResult.CONTINUE;
}
});
// for循环完成之后,finish-tar包输出流
tOut.finish();
}
}
四.tar.gz解压缩
@Test
public void testDeCompressTarGzip() throws IOException {
// 解压文件
Path source = Paths.get("/home/test/output.tar.gz");
// 解压到哪
Path target = Paths.get("/home/test2");
if (Files.notExists(source)) {
throw new IOException("您要解压的文件不存在");
}
// InputStream输入流,以下四个流将tar.gz读取到内存并操作
// BufferedInputStream缓冲输入流
// GzipCompressorInputStream解压输入流
// TarArchiveInputStream解tar包输入流
try (InputStream fi = Files.newInputStream(source);
BufferedInputStream bi = new BufferedInputStream(fi);
GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {
ArchiveEntry entry;
while ((entry = ti.getNextEntry()) != null) {
// 获取解压文件目录,并判断文件是否损坏
Path newPath = zipSlipProtect(entry, target);
if (entry.isDirectory()) {
// 创建解压文件目录
Files.createDirectories(newPath);
} else {
// 再次校验解压文件目录是否存在
Path parent = newPath.getParent();
if (parent != null) {
if (Files.notExists(parent)) {
Files.createDirectories(parent);
}
}
// 将解压文件输入到TarArchiveInputStream,输出到磁盘newPath目录
Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
// 判断压缩文件是否被损坏,并返回该文件的解压目录
private Path zipSlipProtect(ArchiveEntry entry,Path targetDir)
throws IOException {
Path targetDirResolved = targetDir.resolve(entry.getName());
Path normalizePath = targetDirResolved.normalize();
if (!normalizePath.startsWith(targetDir)) {
throw new IOException("压缩文件已被损坏: " + entry.getName());
}
return normalizePath;
}