java實(shí)現(xiàn)多層級(jí)zip解壓的示例代碼
前言
項(xiàng)目中偶然需要,希望能處理嵌套的壓縮包,但是又不希望把文件解壓處理。原本不希望重復(fù)造輪子,但沒(méi)有發(fā)現(xiàn)很好用的現(xiàn)成案例,就簡(jiǎn)單處理了一下。
正文
java做zip解壓一般使用 ZipFile
? 或者 ZipInputStream
?。
在實(shí)際使用中,遇到了zip清單屬性無(wú)法讀取的報(bào)錯(cuò),最終采用了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>
代碼主要為符合業(yè)務(wù)需求而寫,比較簡(jiǎn)陋。支持單次解壓和遞歸解壓,均通過(guò)回調(diào)返回緩沖流(無(wú)法關(guān)閉的緩沖流)。
必須要注意的是,一定不能提前關(guān)閉ZipArchiveInputStream,這個(gè)流一次會(huì)在getNextZipEntry后再次填充。
回調(diào)如果采用字節(jié)對(duì)內(nèi)存的壓力可能會(huì)比較大,所以通過(guò)緩沖流返回?cái)?shù)據(jù)。為防止多人協(xié)作中出現(xiàn)誤關(guān)閉流,使用不關(guān)閉源流的緩沖流工具。
如果有需要解壓指定包,在入?yún)⒓右粋€(gè)filter就可以實(shí)現(xiàn)。
完整代碼實(shí)例
package xxx; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; /** 用于輔助的不關(guān)閉原流的緩沖流 */ public class NoCloseBufferStream extends BufferedInputStream { public NoCloseBufferStream(InputStream in) { super(in); } @Override public void close() throws IOException { //不實(shí)現(xiàn)任何東西就不會(huì)關(guān)閉原流 } }
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; /** * 注意:初始的輸入流是不會(huì)主動(dòng)關(guān)閉的 * * @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 遞歸回調(diào),返回值控制是否向下遞歸 * @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) -> { // 此處決定是否繼續(xù)向下 if (loopCallBack.call(level, path, basePath, is) && path.endsWith(".zip")) { loopUnzip(is, level + 1, basePath + "/" + path, loopCallBack); } }); } /** * 解壓zip,必須是zip結(jié)尾的文件(錯(cuò)誤屬性的文件會(huì)被排除,因?yàn)椴慌懦齤ava也解壓不了) * * @param inputStream 初始輸入流 * @param callBack 回調(diào) * @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(); //過(guò)濾無(wú)用文件 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; } }
到此這篇關(guān)于java實(shí)現(xiàn)多層級(jí)zip解壓的示例代碼的文章就介紹到這了,更多相關(guān)java zip解壓內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring整合quartz做定時(shí)任務(wù)的示例代碼
這篇文章主要介紹了在spring項(xiàng)目使用quartz做定時(shí)任務(wù),首先我這里的項(xiàng)目已經(jīng)是一個(gè)可以跑起來(lái)的完整項(xiàng)目,web.xml里面的配置我就不貼出來(lái)了,具體實(shí)例代碼跟隨小編一起看看吧2022-01-01JAVAEE中用Session簡(jiǎn)單實(shí)現(xiàn)購(gòu)物車功能示例代碼
本篇文章主要介紹了JAVAEE中用Session簡(jiǎn)單實(shí)現(xiàn)購(gòu)物車功能示例代碼,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2017-03-03Spring配置多個(gè)數(shù)據(jù)源并實(shí)現(xiàn)動(dòng)態(tài)切換示例
本篇文章主要介紹了Spring配置多個(gè)數(shù)據(jù)源并實(shí)現(xiàn)動(dòng)態(tài)切換示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-04-04SpringCloud網(wǎng)關(guān)組件zuul實(shí)例解析
這篇文章主要介紹了SpringCloud網(wǎng)關(guān)組件zuul實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03SpringBoot隨機(jī)數(shù)設(shè)置及參數(shù)間引用的操作步驟
在Spring Boot配置文件中設(shè)置屬性時(shí),除了可以像前面示例中顯示的配置屬性值外,還可以使用隨機(jī)值和參數(shù)間引用對(duì)屬性值進(jìn)行設(shè)置。下面給大家介紹SpringBoot參數(shù)間引用隨機(jī)數(shù)設(shè)置的操作步驟,感興趣的朋友一起看看吧2021-06-06使用Java實(shí)現(xiàn)Excel轉(zhuǎn)PDF的示例詳解
在實(shí)際的開發(fā)過(guò)程中,我們常常會(huì)遇到需要將 Excel 文件轉(zhuǎn)換為 PDF 文件的需求,本文為大家介紹一種Java中的常見(jiàn)實(shí)現(xiàn)方式,需要的可以參考一下2025-02-02Mybatis的parameterType造成線程阻塞問(wèn)題分析
這篇文章主要詳細(xì)分析了Mybatis的parameterType造成線程阻塞問(wèn)題,文中有詳細(xì)的解決方法,及相關(guān)的代碼示例,具有一定的參考價(jià)值,感興趣的朋友可以借鑒閱讀2023-06-06