JAVA 解压缩代码写法

package com.chinaunicom.asset.common.utils.compress;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.springframework.util.FileSystemUtils;

import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

@Slf4j
public abstract class AbstractArchive implements Closeable {

    private static final int THRESHOLD_ENTRIES = 10_000;
    private static final int THRESHOLD_SIZE = 1_000_000_000; // 1 GB

    private final Path archive;

    protected AbstractArchive(Path archive) {
        this.archive = archive;
    }

    protected Path getArchive() {
        return archive;
    }

    protected abstract ArchiveEntry nextEntry() throws IOException;

    protected abstract InputStream getInputStream() throws IOException;

    protected abstract boolean skipEntry();

    // 不解压macOS的meta data文件
    protected static boolean isMacOSMetaData(String entryName) {
        if (entryName.startsWith("__MACOSX/")) {
            return true;
        }
        return entryName.endsWith("/.DS_Store") || entryName.equals(".DS_Store");
    }

    public void decompress(Path decompressedPath) throws IOException {

        ArchiveEntry entry;
        int totalSize = 0;
        int archiveEntries = 0;
        while ((entry = nextEntry()) != null) {
            InputStream in = getInputStream();

            if (skipEntry()) {
                continue;
            }

            Path entryDest = decompressedPath.resolve(entry.getName());
            if (entry.isDirectory()) {
                Files.createDirectories(entryDest);

            } else {
                writeToDest(in, entryDest);

                validateTotal(archiveEntries++, decompressedPath);

                totalSize += entry.getSize();
                validateTotalSize(totalSize, decompressedPath);

            }
        }

    }

    public static AbstractArchive newArchive(Path archivePath) throws IOException {
        String extension = FilenameUtils.getExtension(archivePath.getFileName().toString());
        switch (extension.toLowerCase()) {
            case "zip":
                return new ZipArchive(archivePath);
            case "7z":
                return new SevenZArchive(archivePath);
            default:
                throw new IllegalArgumentException("Unsupported Archive Type:" + extension);
        }
    }

    private static void writeToDest(InputStream in, Path entryDest) throws IOException {
        Files.createDirectories(entryDest.getParent());
        try (OutputStream out = Files.newOutputStream(entryDest)) {
            IOUtils.copy(in, out);
        }
    }

    private static void validateTotalSize(int totalSize, Path decompressedPath) {
        if (totalSize > THRESHOLD_SIZE) {
            log.error("文件过大:{},终止解压", totalSize);
            cleanOnFailed(decompressedPath);
            throw new IllegalArgumentException("压缩文件过大,解压失败");
        }
    }

    private static void validateTotal(int archiveEntries, Path decompressedPath) {
        if (archiveEntries > THRESHOLD_ENTRIES) {
            log.error("文件数量过多,终止解压");
            cleanOnFailed(decompressedPath);
            throw new IllegalArgumentException("压缩文件内文件过多,解压失败");
        }
    }

    private static void cleanOnFailed(Path targetPath) {
        try {
            FileSystemUtils.deleteRecursively(targetPath);
        } catch (IOException e) {
            log.error("清理文件失败:{}", e.getMessage(), e);
        }
    }
}

 

package com.chinaunicom.asset.common.utils.compress;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveInputStream;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

@Slf4j
public class ZipArchive extends AbstractArchive {

    private final ArchiveInputStream inputStream;
    private ArchiveEntry currentEntry;

    public ZipArchive(Path archivePath) throws IOException {
        super(archivePath);
        this.inputStream = new ZipArchiveInputStream(Files.newInputStream(getArchive()));
    }

    @Override
    protected ArchiveEntry nextEntry() throws IOException {
        currentEntry = inputStream.getNextEntry();
        return currentEntry;
    }

    @Override
    protected InputStream getInputStream() {
        return inputStream;
    }

    @Override
    protected boolean skipEntry() {
        // 跳过无法读取的文件
        if (!inputStream.canReadEntryData(currentEntry)) {
            log.warn("压缩包内存在无法读取的文件");
            return true;
        }
        return isMacOSMetaData(currentEntry.getName());
    }

    @Override
    public void close() throws IOException {
        this.inputStream.close();
    }
}

 

package com.chinaunicom.asset.common.utils.compress;

import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class SevenZArchive extends AbstractArchive {

    private final SevenZFile sevenZFile;
    private SevenZArchiveEntry currentEntry;

    public SevenZArchive(Path archivePath) throws IOException {
        super(archivePath);
        this.sevenZFile = new SevenZFile(Files.newByteChannel(getArchive()));
    }

    @Override
    protected ArchiveEntry nextEntry() throws IOException {
        currentEntry = sevenZFile.getNextEntry();
        return currentEntry;
    }

    @Override
    protected InputStream getInputStream() throws IOException {
        return sevenZFile.getInputStream(currentEntry);
    }

    @Override
    protected boolean skipEntry() {
        return isMacOSMetaData(currentEntry.getName());
    }

    @Override
    public void close() throws IOException {
        this.sevenZFile.close();
    }
}

 

package com.chinaunicom.asset.common.utils;

import cn.hutool.core.lang.UUID;
import com.chinaunicom.asset.common.exception.BaseException;
import com.chinaunicom.asset.common.utils.compress.AbstractArchive;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;

@Slf4j
public class ArchiveFiles {

    private ArchiveFiles() {
    }

    public static Path decompress(Path archivePath) {
        // 检查文件存在
        if (!Files.exists(archivePath) || Files.isDirectory(archivePath)) {
            throw new IllegalArgumentException("压缩文件不存在");
        }

        // 在压缩文件的父目录生成目录,以存储解压后的文件
        String decompressedDir = UUID.randomUUID().toString(true);
        Path decompressedPath = archivePath.getParent().resolve(decompressedDir);

        try (AbstractArchive archive = AbstractArchive.newArchive(archivePath)) {
            archive.decompress(decompressedPath);
        } catch (IOException e) {
            log.error(e.getMessage());
            throw new BaseException("解压文件失败");
        } catch (Exception e) {
            throw new BaseException(e);
        }

        return decompressedPath;
    }

}

 

posted on 2022-12-05 10:07  MaXianZhe  阅读(156)  评论(0编辑  收藏  举报

导航