首页 > 其他分享 >自定义多层级zip解压工具

自定义多层级zip解压工具

时间:2024-12-23 10:53:32浏览次数:3  
标签:解压 自定义 zip inputStream IOException import throws

自定义多层级zip解压工具

前言

  项目中偶然需要,希望能处理嵌套的压缩包,但是又不希望把文件解压处理。原本不希望重复造轮子,但没有发现很好用的现成案例,就简单处理了一下。

正文

  java做zip解压一般使用 ZipFile​ 或者 ZipInputStream​。

  在实际使用中,遇到了zip清单属性无法读取的报错,最终采用了apache的ZipArchiveInputStream。主要是allowStoredEntriesWithDataDescriptor​属性。

  代码完整使用的依赖如下:

	    <dependency>
	      <groupId>org.apache.commons</groupId>
	      <artifactId>commons-compress</artifactId>
	      <version>1.19</version>
	    </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.26</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.26</version>
        </dependency>

  代码主要为符合业务需求而写,比较简陋。支持单次解压和递归解压,均通过回调返回缓冲流(无法关闭的缓冲流)。

  必须要注意的是,一定不能提前关闭ZipArchiveInputStream,这个流一次会在getNextZipEntry后再次填充。

  回调如果采用字节对内存的压力可能会比较大,所以通过缓冲流返回数据。为防止多人协作中出现误关闭流,使用不关闭源流的缓冲流工具。

  如果有需要解压指定包,在入参加一个filter就可以实现。

完整代码实例

package xxx;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

/**

  • 用于辅助的不关闭原流的缓冲流
    */
    public class NoCloseBufferStream extends BufferedInputStream {

    public NoCloseBufferStream(InputStream in) {
    super(in);
    }

    @Override
    public void close() throws IOException {
    //不实现任何东西就不会关闭原流
    }

}

package xxx; //your package

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.CharsetUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;

/**
 * 注意:初始的输入流是不会主动关闭的
 *
 * @author 铁流
 */
@Slf4j
public class UnZipUtil {

    public static void main(String[] args) throws IOException {

        try (InputStream inputStream = Files.newInputStream(new File("/Users/tieliu/Desktop/test/aaaa.zip").toPath());) {
            loopUnzip(inputStream, (level, path, basePath, is) -> {
                is.close();
                log.info(" level: {},path: {},basePath: {}", level, path, basePath);
                return true;
            });
        }
    }

    /**
     * 递归解压zip,只能解压zip后缀名的压缩文件
     *
     * @param inputStream  初始文件输入流
     * @param loopCallBack 递归回调,返回值控制是否向下递归
     * @throws IOException 文件流异常
     */
    public static void loopUnzip(InputStream inputStream, LoopCallBack loopCallBack) throws IOException {
        loopUnzip(inputStream, 0, "", loopCallBack);
    }

    private static void loopUnzip(InputStream inputStream, int level, String basePath, LoopCallBack loopCallBack) throws IOException {
        decompress(inputStream, (path, is) -> {
            // 此处决定是否继续向下
            if (loopCallBack.call(level, path, basePath, is) && path.endsWith(".zip")) {
                loopUnzip(is, level + 1, basePath + "/" + path, loopCallBack);
            }
        });
    }

    /**
     * 解压zip,必须是zip结尾的文件(错误属性的文件会被排除,因为不排除java也解压不了)
     *
     * @param inputStream 初始输入流
     * @param callBack    回调
     * @throws IOException io异常
     */
    public static void decompress(InputStream inputStream, CallBack callBack) throws IOException {
        try (NoCloseBufferStream bufferedInputStream = new NoCloseBufferStream(inputStream);
             ZipArchiveInputStream zipInputStream = new ZipArchiveInputStream(bufferedInputStream, CharsetUtil.defaultCharset().name(), true, true)) {
            decompress(zipInputStream, callBack);
        }
    }

    public static void decompress(byte[] bytes, CallBack callBack) throws IOException {
        try (ByteArrayInputStream inputStream = IoUtil.toStream(bytes);) {
            bytes = null;
            decompress(inputStream, callBack);
        }
    }

    private static void decompress(ZipArchiveInputStream inputStream, CallBack callBack) throws IOException {
        ZipArchiveEntry nextEntry = inputStream.getNextZipEntry();
        while (nextEntry != null) {
            final String name = nextEntry.getName();
            //过滤无用文件
            if (!name.startsWith("__MACOSX") && !name.contains(".DS_Store") && !name.contains("Thumbs.db") && !name.startsWith("._")) {
                if (!nextEntry.isDirectory()) {
                    callBack.call(name, new NoCloseBufferStream(inputStream));
                }
            }
            nextEntry = inputStream.getNextZipEntry();
        }
    }

    @FunctionalInterface
    public static interface CallBack {
        void call(String relativePath, InputStream is) throws IOException;
    }

    @FunctionalInterface
    public static interface LoopCallBack {
        boolean call(int level, String relativePath, String basePath, InputStream is) throws IOException;
    }

}

标签:解压,自定义,zip,inputStream,IOException,import,throws
From: https://www.cnblogs.com/dawnings/p/18623456/zi-ding-yi-duo-ceng-ji-zipjie-ya-gong-ju-z1sx8

相关文章

  • 【随手记录】Apache POI 报错:Zip bomb detected! The file would exceed the max. rat
    1、背景:使用POI解析Excel报错:Zipbombdetected!Thefilewouldexceedthemax.ratioofcompressedfilesizetothesizeoftheexpandeddata。Thismayindicatethatthefileisusedtoinflatememoryusageandthiscouldposeasecurityrisk.Youcanadj......
  • 使用HTML5自定义一个复选框
    在HTML5中,虽然我们不能直接自定义原生的复选框样式,但我们可以使用一些技巧来模拟自定义复选框。以下是一个简单的示例,说明如何使用HTML、CSS和JavaScript来创建一个自定义复选框。HTML:<labelclass="custom-checkbox"><inputtype="checkbox"hidden><spanclass="ch......
  • 使用HTML5自定义一个下拉框
    在HTML5中,你可以使用原生的<select>和<option>元素来创建下拉框,但如果你想自定义其样式和行为,你可能需要使用一些额外的HTML、CSS和JavaScript。以下是一个简单的示例,展示如何使用HTML、CSS和JavaScript创建一个自定义的下拉框:HTML<divclass="custom-dropdown"><inputtype......
  • 在鸿蒙NEXT中实现完全自定义导航栏
    在日常app开发中,导航栏扮演着重要的角色。鸿蒙提供了系统导航栏Navigation,它支持很多属性的修改,但是应用需求更加灵活多变,比如有的导航栏有背景图片,有的导航栏要求渐变色,有的导航栏需要随时隐藏和显示等等。遇到这些需求系统的Navigation就无法实现,这时候我们就需要自定义导航栏......
  • C# winform自定义图片空间 缩放、拖拽、绘画
    首先简单介绍一下图片自定义的功能1、滑动鼠标滚轮缩放图片;2、按住鼠标左键拖拽图片;3、可选绘画矩形、圆、直线、画笔;4、可选连续绘画、单次绘画;5、每次绘画通过事件返回起点坐标、终点坐标;6、右击可重置图片大小(适应窗口)、原始尺寸(100%)、另存当前图片、清除绘画;7、For......
  • 在SpringBoot项目中接入sensitive-word实现敏感词过滤(DFA算法、为敏感词打上标签、忽
    文章目录1.前言2.敏感词过滤的常见解决方案3.DFA算法3.1什么是DFA算法3.2DFA算法的原理3.2.1数据是如何存储的3.2.2数据是如何检索的3.3DFA算法的应用场景4.sensitive-word简介4.1什么是sensitive-word4.2sensitive-word的官网4.3sensitive-word的性能5.......
  • 如何使用Wireshark自定义捕获列表
    简述Wireshark是一款强大的网络协议分析工具,它不仅可以捕获网络流量,还能帮助用户深入分析和解读各种网络协议。为了让数据包分析更加高效,Wireshark提供了自定义捕获列表的功能,使用户能够根据个人需求定制和优化界面布局,显示关键信息。在本文中,我们将介绍如何自定义Wiresha......
  • 如何在易优CMS中自定义ad标签中的变量名?
    在易优CMS中,如果你希望在ad标签中自定义变量名,可以使用id属性。通过设置id属性,你可以将默认的$field变量名替换为你自定义的变量名。以下是一个具体的示例:{eyou:adaid='37'id='field1'}<ahref="{$field1.links}"{$field1.target}><imgalt="{$field1.title}......
  • 如何在易优CMS中定义并使用自定义变量?
    在易优CMS中,你可以使用assign标签来定义自定义变量,并在其他标签中引用这些变量。以下是一个具体的示例:{eyou:assignname='typeid'value='5'/}{eyou:typetypeid='$typeid'}<ahref="{$field.typeurl}">{$field.typename}</a>{/eyou:type}{eyou:cha......
  • Redis篇-13--数据结构篇5--List内存模型(LinkedList,zipList,quicklist,Listpack,内存对齐,
    Redis的List(列表)数据类型是一个双向链表,允许从两端高效地插入和删除元素。为了提高性能和内存利用率,Redis对List进行了多种优化。特别是在Redis3.2版本中引入的quicklist结构,和Redis6.2版本中引入Listpack结构(替代之前的ziplist压缩列表),逐步提升List的性能。简单概括如下......