java使用zstd压缩算法实现文件压缩和解压记录

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>
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容