使用Java API进行tar.gz文件及文件夹压缩解压缩

网友投稿 1305 2022-05-29

在java(JDK)中我们可以使用ZipOutputStream去创建zip压缩文件,(参考我之前写的文章 使用java API进行zip递归压缩文件夹以及解压 ),也可以使用GZIPOutputStream去创建gzip(gz)压缩文件,但是java中没有一种官方的API可以去创建tar.gz文件。所以我们需要使用到第三方库Apache Commons Compress去创建.tar.gz文件。

在pom.xml中,我们可以通过如下的maven坐标引入commons-compress。

org.apache.commons commons-compress 1.20

1

2

3

4

5

解释说明

tar文件准确的说是打包文件,将文件打包到一个tar文件中,文件名后缀是.tar

Gzip是将文件的存储空间压缩保存,文件名后缀是.gz

tar.gz或.tgz通常是指将文件打包到一个tar文件中,并将它使用Gzip进行压缩。

如果您阅读完本文觉得对您有帮助的话,请给我一个赞,您的支持是我不竭的创作动力!

一、将两个文件打包到tar.gz

下面的这个例子是将2个文件打包为tar.gz压缩文件。下文代码中的流操作使用了try-with-resources语法,所以不用写代码手动的close流。

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 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(); } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

将file-a.xml和file-b.txt打包到output.tar文件中,并使用gzip对这个tar包进行压缩。可以使用如下命令查看tar包里面包含的文件。

$ tar -tvf /home/test/output.tar.gz -rw-r--r-- 0/0 23546 2020-08-17 12:07 file-a.xml -rw-r--r-- 0/0 34 2020-08-17 12:36 file-b.txt

1

2

3

二、将一个文件夹压缩为tar.gz

下面的例子将一个文件夹,包含其子文件夹的文件或子目录,打包为tar,并使用gzip进行压缩。最终成为一个tar.gz打包压缩文件。

其核心原理是:使用到Files.walkFileTree依次遍历文件目录树中的文件,将其一个一个的添加到TarArchiveOutputStream.输出流。

@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() { //当成功访问到一个文件 @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(); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

三、解压tar.gz压缩文件

下面一个例子说明如何解压一个tar.gz文件,具体内容请看代码注释。

@Test 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; }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

使用Java API进行tar.gz文件及文件夹压缩解压缩

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

API Java 弹性文件服务

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Oracle和mysql容易混的误区
下一篇:萌妹子Python入门指北(二)
相关文章