java實現(xiàn)多層級zip解壓的示例代碼
前言
項目中偶然需要,希望能處理嵌套的壓縮包,但是又不希望把文件解壓處理。原本不希望重復(fù)造輪子,但沒有發(fā)現(xiàn)很好用的現(xiàn)成案例,就簡單處理了一下。
正文
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>代碼主要為符合業(yè)務(wù)需求而寫,比較簡陋。支持單次解壓和遞歸解壓,均通過回調(diào)返回緩沖流(無法關(guān)閉的緩沖流)。
必須要注意的是,一定不能提前關(guān)閉ZipArchiveInputStream,這個流一次會在getNextZipEntry后再次填充。
回調(diào)如果采用字節(jié)對內(nèi)存的壓力可能會比較大,所以通過緩沖流返回數(shù)據(jù)。為防止多人協(xié)作中出現(xiàn)誤關(guān)閉流,使用不關(guān)閉源流的緩沖流工具。
如果有需要解壓指定包,在入?yún)⒓右粋€filter就可以實現(xiàn)。
完整代碼實例
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 {
//不實現(xiàn)任何東西就不會關(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;
/**
* 注意:初始的輸入流是不會主動關(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é)尾的文件(錯誤屬性的文件會被排除,因為不排除java也解壓不了)
*
* @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();
//過濾無用文件
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實現(xiàn)多層級zip解壓的示例代碼的文章就介紹到這了,更多相關(guān)java zip解壓內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis中批量插入和批量更新的實現(xiàn)方法詳解
這篇文章主要介紹了MyBatis中批量插入和批量更新的實現(xiàn)方法,在日常開發(fā)中有時候需要從A數(shù)據(jù)庫提取大量數(shù)據(jù)同步到B系統(tǒng),這種情況自然是需要批量操作才行,感興趣想要詳細(xì)了解可以參考下文2023-05-05
全網(wǎng)最全最細(xì)的jmeter接口測試教程以及接口測試流程(入門教程)
本文主要介紹了全網(wǎng)最全最細(xì)的jmeter接口測試教程以及接口測試流程,文中介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-11-11
深入探究TimSort對歸并排序算法的優(yōu)化及Java實現(xiàn)
這篇文章主要介紹了TimSort歸并排序的優(yōu)化及Java實現(xiàn),TimSort 是一個歸并排序做了大量優(yōu)化的版本,需要的朋友可以參考下2016-05-05
關(guān)于@Autowired注解爆紅的原因分析及解決過程
這篇文章主要介紹了關(guān)于@Autowired注解爆紅的原因分析及解決過程,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-06-06
Mybatis-Plus同時使用邏輯刪除和唯一索引的問題及解決辦法(報數(shù)據(jù)重復(fù)Duplicate entry的
在開發(fā)中,我們經(jīng)常會有邏輯刪除和唯一索引同時使用的情況,但當(dāng)使用mybatis plus時,如果同時使用邏輯刪除和唯一索引,會報數(shù)據(jù)重復(fù)Duplicate entry的問題,如何解決這個問題呢,小編給大家分享Mybatis-Plus同時使用邏輯刪除和唯一索引的問題及解決辦法,一起看看吧2023-11-11
SpringBoot+ENC實現(xiàn)密鑰加密的使用示例
本文主要介紹了SpringBoot+ENC實現(xiàn)密鑰加密的使用示例,主要是為了將配置信息從應(yīng)用程序代碼中分離出來,以提高安全性和可維護(hù)性,感興趣的可以了解一下2024-07-07

