diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java index fb9e0c606..a35288241 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Extractor.java @@ -1,9 +1,11 @@ package cn.hutool.extra.compress.extractor; +import cn.hutool.core.text.StrUtil; import org.apache.commons.compress.archivers.ArchiveEntry; import java.io.Closeable; import java.io.File; +import java.io.InputStream; import java.util.function.Predicate; /** @@ -31,6 +33,24 @@ public interface Extractor extends Closeable { */ void extract(File targetDir, Predicate predicate); + /** + * 获取指定名称的文件流 + * + * @param entryName entry名称 + * @return 文件流,无文件返回{@code null} + */ + default InputStream get(final String entryName) { + return getFirst((entry) -> StrUtil.equals(entryName, entry.getName())); + } + + /** + * 获取满足指定过滤要求的压缩包内的第一个文件流 + * + * @param predicate 用于指定需要释放的文件,null表示不过滤。当{@link Predicate#test(Object)}为{@code true}返回对应流。 + * @return 满足过滤要求的第一个文件的流, 无满足条件的文件返回{@code null} + */ + InputStream getFirst(final Predicate predicate); + /** * 无异常关闭 */ diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Seven7EntryInputStream.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Seven7EntryInputStream.java index e497de263..d30d5d8f8 100644 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Seven7EntryInputStream.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/Seven7EntryInputStream.java @@ -25,8 +25,18 @@ public class Seven7EntryInputStream extends InputStream { * @param entry {@link SevenZArchiveEntry} */ public Seven7EntryInputStream(final SevenZFile sevenZFile, final SevenZArchiveEntry entry) { + this(sevenZFile, entry.getSize()); + } + + /** + * 构造 + * + * @param sevenZFile {@link SevenZFile} + * @param size 读取长度 + */ + public Seven7EntryInputStream(final SevenZFile sevenZFile, final long size) { this.sevenZFile = sevenZFile; - this.size = entry.getSize(); + this.size = size; } @Override diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/SevenZExtractor.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/SevenZExtractor.java index f452e5523..adf8cc73c 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/SevenZExtractor.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/SevenZExtractor.java @@ -1,10 +1,9 @@ package cn.hutool.extra.compress.extractor; -import cn.hutool.core.io.file.FileUtil; import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.io.file.FileUtil; import cn.hutool.core.lang.Assert; -import cn.hutool.core.text.StrUtil; import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry; import org.apache.commons.compress.archivers.sevenz.SevenZFile; @@ -103,13 +102,7 @@ public class SevenZExtractor implements Extractor, RandomAccess { } } - /** - * 获取满足指定过滤要求的压缩包内的第一个文件流 - * - * @param predicate 用于指定需要释放的文件,null表示不过滤。当{@link Predicate#test(Object)}为{@code true}返回对应流。 - * @return 满足过滤要求的第一个文件的流, 无满足条件的文件返回{@code null} - * @since 5.7.14 - */ + @Override public InputStream getFirst(final Predicate predicate) { final SevenZFile sevenZFile = this.sevenZFile; for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) { @@ -121,6 +114,7 @@ public class SevenZExtractor implements Extractor, RandomAccess { } try { + // 此处使用查找entry对应Stream方式,由于只调用一次,也只遍历一次 return sevenZFile.getInputStream(entry); } catch (final IOException e) { throw new IORuntimeException(e); @@ -130,17 +124,6 @@ public class SevenZExtractor implements Extractor, RandomAccess { return null; } - /** - * 获取指定名称的文件流 - * - * @param entryName entry名称 - * @return 文件流,无文件返回{@code null} - * @since 5.7.14 - */ - public InputStream get(final String entryName) { - return getFirst((entry) -> StrUtil.equals(entryName, entry.getName())); - } - /** * 释放(解压)到指定目录 * @@ -153,7 +136,7 @@ public class SevenZExtractor implements Extractor, RandomAccess { final SevenZFile sevenZFile = this.sevenZFile; SevenZArchiveEntry entry; File outItemFile; - while (null != (entry = this.sevenZFile.getNextEntry())) { + while (null != (entry = sevenZFile.getNextEntry())) { if (null != predicate && false == predicate.test(entry)) { continue; } @@ -164,6 +147,7 @@ public class SevenZExtractor implements Extractor, RandomAccess { outItemFile.mkdirs(); } else if (entry.hasStream()) { // 读取entry对应数据流 + // 此处直接读取而非调用sevenZFile.getInputStream(entry),因为此方法需要遍历查找entry对应位置,性能不好。 FileUtil.writeFromStream(new Seven7EntryInputStream(sevenZFile, entry), outItemFile); } else { // 无数据流的文件创建为空文件 diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java index c1b91abee..459d55869 100755 --- a/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java +++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/extractor/StreamExtractor.java @@ -68,7 +68,7 @@ public class StreamExtractor implements Extractor { * @param in 包流 */ public StreamExtractor(final Charset charset, final String archiverName, InputStream in) { - if(in instanceof ArchiveInputStream){ + if (in instanceof ArchiveInputStream) { this.in = (ArchiveInputStream) in; return; } @@ -78,7 +78,7 @@ public class StreamExtractor implements Extractor { in = IoUtil.toBuffered(in); if (StrUtil.isBlank(archiverName)) { this.in = factory.createArchiveInputStream(in); - } else if("tgz".equalsIgnoreCase(archiverName) || "tar.gz".equalsIgnoreCase(archiverName)){ + } else if ("tgz".equalsIgnoreCase(archiverName) || "tar.gz".equalsIgnoreCase(archiverName)) { //issue#I5J33E,支持tgz格式解压 try { this.in = new TarArchiveInputStream(new GzipCompressorInputStream(in)); @@ -95,6 +95,29 @@ public class StreamExtractor implements Extractor { } } + @Override + public InputStream getFirst(final Predicate predicate) { + final ArchiveInputStream in = this.in; + ArchiveEntry entry; + try { + while (null != (entry = in.getNextEntry())) { + if (null != predicate && false == predicate.test(entry)) { + continue; + } + if (entry.isDirectory() || false == in.canReadEntryData(entry)) { + // 目录或无法读取的文件直接跳过 + continue; + } + + return in; + } + } catch (final IOException e) { + throw new IORuntimeException(e); + } + + return null; + } + /** * 释放(解压)到指定目录,结束后自动关闭流,此方法只能调用一次 *