java生成tar.zst和解压实现代码如下,main方法里有压缩和解压示例:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
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 com.github.luben.zstd.ZstdInputStream;
import com.github.luben.zstd.ZstdOutputStream;
import ly.mp.project.common.util.LogUtils;
public class ZstdUtil {
private static final int BUFFER_SIZE = 256 * 1024; // 256kb
/**
* 流式压缩:将 sourceFolder 打包压缩为 archive.tar.zst
* 内存占用极低,适合大文件夹
*
* @param sourceFolder 要压缩的文件夹路径
* @param outputFile 输出的 .tar.zst 文件路径
* @throws IOException IO异常
*/
public static void compressFolderToZstdTarStreaming(Path sourceFolder, Path outputFile) throws IOException {
// 直接用 ZstdOutputStream 包装 FileOutputStream
try (ZstdOutputStream zstdOut = new ZstdOutputStream(Files.newOutputStream(outputFile));
TarArchiveOutputStream tarOut = new TarArchiveOutputStream(zstdOut)) {
// 配置 TAR 输出流支持长文件名和大文件
tarOut.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
tarOut.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
// 遍历文件夹,逐个文件写入 TAR 流
Files.walkFileTree(sourceFolder, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
// 计算相对路径作为 TAR 内的条目名
String entryName = sourceFolder.relativize(file).toString()
.replace("\\", "/"); // Windows 路径转 Unix 格式
// 创建 TAR 条目
TarArchiveEntry entry = new TarArchiveEntry(file.toFile(), entryName);
// 写入条目头
tarOut.putArchiveEntry(entry);
// 流式拷贝文件内容到 TAR 输出流(底层会经过 ZSTD 压缩)
try (InputStream fileIn = Files.newInputStream(file)) {
byte[] buffer = new byte[BUFFER_SIZE]; // 8KB 缓冲区
int len;
while ((len = fileIn.read(buffer)) != -1) {
tarOut.write(buffer, 0, len);
}
}
// 关闭当前条目
tarOut.closeArchiveEntry();
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
// 打印错误但继续处理其他文件
LogUtils.info("处理文件失败: " + file + " - " + exc.getMessage());
throw exc;
//return FileVisitResult.CONTINUE;
}
});
// 注意:tarOut 和 zstdOut 会自动关闭(try-with-resources)
}
}
/**
* 流式解压:将 archive.tar.zst 解压到目标文件夹
* 内存占用极低,适合大文件
*
* @param archiveFile 要解压的 .tar.zst 文件路径
* @param targetFolder 解压目标文件夹路径
* @throws IOException IO异常
*/
public static void decompressZstdTarToFolder(Path archiveFile, Path targetFolder) throws IOException {
// 确保目标文件夹存在
Files.createDirectories(targetFolder);
// 用 ZstdInputStream 解压,再交给 TarArchiveInputStream 解析
try (InputStream fileIn = Files.newInputStream(archiveFile);
ZstdInputStream zstdIn = new ZstdInputStream(fileIn);
TarArchiveInputStream tarIn = new TarArchiveInputStream(zstdIn)) {
TarArchiveEntry entry;
while ((entry = (TarArchiveEntry) tarIn.getNextEntry()) != null) {
// 防止路径穿越攻击(../../etc/passwd)
Path targetPath = targetFolder.resolve(entry.getName()).normalize();
if (!targetPath.startsWith(targetFolder)) {
throw new IOException("非法的 TAR 条目路径: " + entry.getName());
}
if (entry.isDirectory()) {
// 目录:创建即可
Files.createDirectories(targetPath);
} else {
// 文件:确保父目录存在,再流式写入
Files.createDirectories(targetPath.getParent());
try (OutputStream fileOut = Files.newOutputStream(targetPath)) {
byte[] buffer = new byte[BUFFER_SIZE];
int len;
while ((len = tarIn.read(buffer)) != -1) {
fileOut.write(buffer, 0, len);
}
}
// 可选:还原文件权限(Linux 环境有效)
// Files.setPosixFilePermissions(targetPath, ...);
}
LogUtils.info("解压: " + entry.getName());
}
}
}
// 使用示例
public static void main(String[] args) {
// ========== 压缩示例 ==========
try {
Path source = Paths.get("D:\\tmp\\zipTest\\source");
Path output = Paths.get("D:\\tmp\\zipTest\\targert\\archive.tar.zst");
compressFolderToZstdTarStreaming(source, output);
System.out.println("压缩完成: " + output.toAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
// ========== 解压示例 ==========
try {
Path archive = Paths.get("D:\\tmp\\zipTest\\targert\\archive.tar.zst");
Path targetFolder = Paths.get("D:\\tmp\\zipTest\\unzip");
decompressZstdTarToFolder(archive, targetFolder);
System.out.println("解压完成: " + targetFolder.toAbsolutePath());
} catch (IOException e) {
e.printStackTrace();
}
// ========== 命令行参考 ==========
// 解压:zstd -dc 文件名.tar.zst | tar -xvf -
// 解压:unzstd -c 文件名.tar.zst | tar -xvf -
// 压缩:tar --zstd -cvf 文件名.tar.zst /要打包的目录
}
}
补充 maven 依赖包引入:
<!-- 引入zstd和lz4压缩算法 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.28.0</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/com.github.luben/zstd-jni -->
<dependency>
<groupId>com.github.luben</groupId>
<artifactId>zstd-jni</artifactId>
<version>1.5.7-13</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.lz4/lz4-java -->
<dependency>
<groupId>org.lz4</groupId>
<artifactId>lz4-java</artifactId>
<version>1.8.1</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.20.0</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.22.0</version>
<scope>compile</scope>
</dependency>
<!-- Source: https://mvnrepository.com/artifact/org.bouncycastle/bcprov-jdk15on -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version>
<scope>compile</scope>
</dependency>