This commit is contained in:
Looly 2023-03-22 00:52:52 +08:00
parent 9d350acfb3
commit ea6eb422b3
4 changed files with 61 additions and 24 deletions

View File

@ -1,9 +1,11 @@
package cn.hutool.extra.compress.extractor; package cn.hutool.extra.compress.extractor;
import cn.hutool.core.text.StrUtil;
import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveEntry;
import java.io.Closeable; import java.io.Closeable;
import java.io.File; import java.io.File;
import java.io.InputStream;
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
@ -31,6 +33,24 @@ public interface Extractor extends Closeable {
*/ */
void extract(File targetDir, Predicate<ArchiveEntry> predicate); void extract(File targetDir, Predicate<ArchiveEntry> 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<ArchiveEntry> predicate);
/** /**
* 无异常关闭 * 无异常关闭
*/ */

View File

@ -25,8 +25,18 @@ public class Seven7EntryInputStream extends InputStream {
* @param entry {@link SevenZArchiveEntry} * @param entry {@link SevenZArchiveEntry}
*/ */
public Seven7EntryInputStream(final SevenZFile sevenZFile, final SevenZArchiveEntry entry) { 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.sevenZFile = sevenZFile;
this.size = entry.getSize(); this.size = size;
} }
@Override @Override

View File

@ -1,10 +1,9 @@
package cn.hutool.extra.compress.extractor; package cn.hutool.extra.compress.extractor;
import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileUtil;
import cn.hutool.core.lang.Assert; 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.ArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry; import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.sevenz.SevenZFile; import org.apache.commons.compress.archivers.sevenz.SevenZFile;
@ -103,13 +102,7 @@ public class SevenZExtractor implements Extractor, RandomAccess {
} }
} }
/** @Override
* 获取满足指定过滤要求的压缩包内的第一个文件流
*
* @param predicate 用于指定需要释放的文件null表示不过滤{@link Predicate#test(Object)}{@code true}返回对应流
* @return 满足过滤要求的第一个文件的流, 无满足条件的文件返回{@code null}
* @since 5.7.14
*/
public InputStream getFirst(final Predicate<ArchiveEntry> predicate) { public InputStream getFirst(final Predicate<ArchiveEntry> predicate) {
final SevenZFile sevenZFile = this.sevenZFile; final SevenZFile sevenZFile = this.sevenZFile;
for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) { for (final SevenZArchiveEntry entry : sevenZFile.getEntries()) {
@ -121,6 +114,7 @@ public class SevenZExtractor implements Extractor, RandomAccess {
} }
try { try {
// 此处使用查找entry对应Stream方式由于只调用一次也只遍历一次
return sevenZFile.getInputStream(entry); return sevenZFile.getInputStream(entry);
} catch (final IOException e) { } catch (final IOException e) {
throw new IORuntimeException(e); throw new IORuntimeException(e);
@ -130,17 +124,6 @@ public class SevenZExtractor implements Extractor, RandomAccess {
return null; 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; final SevenZFile sevenZFile = this.sevenZFile;
SevenZArchiveEntry entry; SevenZArchiveEntry entry;
File outItemFile; File outItemFile;
while (null != (entry = this.sevenZFile.getNextEntry())) { while (null != (entry = sevenZFile.getNextEntry())) {
if (null != predicate && false == predicate.test(entry)) { if (null != predicate && false == predicate.test(entry)) {
continue; continue;
} }
@ -164,6 +147,7 @@ public class SevenZExtractor implements Extractor, RandomAccess {
outItemFile.mkdirs(); outItemFile.mkdirs();
} else if (entry.hasStream()) { } else if (entry.hasStream()) {
// 读取entry对应数据流 // 读取entry对应数据流
// 此处直接读取而非调用sevenZFile.getInputStream(entry)因为此方法需要遍历查找entry对应位置性能不好
FileUtil.writeFromStream(new Seven7EntryInputStream(sevenZFile, entry), outItemFile); FileUtil.writeFromStream(new Seven7EntryInputStream(sevenZFile, entry), outItemFile);
} else { } else {
// 无数据流的文件创建为空文件 // 无数据流的文件创建为空文件

View File

@ -68,7 +68,7 @@ public class StreamExtractor implements Extractor {
* @param in 包流 * @param in 包流
*/ */
public StreamExtractor(final Charset charset, final String archiverName, InputStream in) { public StreamExtractor(final Charset charset, final String archiverName, InputStream in) {
if(in instanceof ArchiveInputStream){ if (in instanceof ArchiveInputStream) {
this.in = (ArchiveInputStream) in; this.in = (ArchiveInputStream) in;
return; return;
} }
@ -78,7 +78,7 @@ public class StreamExtractor implements Extractor {
in = IoUtil.toBuffered(in); in = IoUtil.toBuffered(in);
if (StrUtil.isBlank(archiverName)) { if (StrUtil.isBlank(archiverName)) {
this.in = factory.createArchiveInputStream(in); 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格式解压 //issue#I5J33E支持tgz格式解压
try { try {
this.in = new TarArchiveInputStream(new GzipCompressorInputStream(in)); this.in = new TarArchiveInputStream(new GzipCompressorInputStream(in));
@ -95,6 +95,29 @@ public class StreamExtractor implements Extractor {
} }
} }
@Override
public InputStream getFirst(final Predicate<ArchiveEntry> 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;
}
/** /**
* 释放解压到指定目录结束后自动关闭流此方法只能调用一次 * 释放解压到指定目录结束后自动关闭流此方法只能调用一次
* *