!904 CompressUtil 新增 stripComponents 参数

Merge pull request !904 from 不忘初心/v5-dev
This commit is contained in:
Looly 2022-12-27 12:09:22 +00:00 committed by Gitee
commit 5d62517ad3
No known key found for this signature in database
GPG Key ID: 173E9B9CA92EEF8F
4 changed files with 120 additions and 36 deletions

View File

@ -1,10 +1,13 @@
package cn.hutool.extra.compress.extractor;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Filter;
import cn.hutool.core.util.StrUtil;
import org.apache.commons.compress.archivers.ArchiveEntry;
import java.io.Closeable;
import java.io.File;
import java.util.List;
/**
* 归档数据解包封装用于将ziptar等包解包为文件
@ -19,7 +22,7 @@ public interface Extractor extends Closeable {
*
* @param targetDir 目标目录
*/
default void extract(File targetDir){
default void extract(File targetDir) {
extract(targetDir, null);
}
@ -29,7 +32,48 @@ public interface Extractor extends Closeable {
* @param targetDir 目标目录
* @param filter 解压文件过滤器用于指定需要释放的文件{@code null}表示不过滤{@link Filter#accept(Object)}为true时释放
*/
void extract(File targetDir, Filter<ArchiveEntry> filter);
default void extract(File targetDir, Filter<ArchiveEntry> filter) {
this.extract(targetDir, 0, filter);
}
/**
* 释放解压到指定目录结束后自动关闭流此方法只能调用一次
*
* @param targetDir 目标目录
* @param stripComponents 清除(剥离)压缩包里面的 n 级文件夹名
*/
default void extract(File targetDir, int stripComponents) {
this.extract(targetDir, stripComponents, null);
}
/**
* 释放解压到指定目录结束后自动关闭流此方法只能调用一次
*
* @param targetDir 目标目录
* @param stripComponents 清除(剥离)压缩包里面的 n 级文件夹名
* @param filter 解压文件过滤器用于指定需要释放的文件{@code null}表示不过滤{@link Filter#accept(Object)}为true时释放
*/
void extract(File targetDir, int stripComponents, Filter<ArchiveEntry> filter);
/**
* 剥离名称
*
* @param name 文件名
* @param stripComponents 剥离层级
* @return 剥离后的文件名
*/
default String stripName(String name, int stripComponents) {
if (stripComponents <= 0) {
return name;
}
List<String> nameList = StrUtil.splitTrim(name, StrUtil.SLASH);
int size = nameList.size();
if (size > stripComponents) {
nameList = CollUtil.sub(nameList, stripComponents, size);
return CollUtil.join(nameList, StrUtil.SLASH);
}
return null;
}
/**
* 无异常关闭

View File

@ -30,7 +30,7 @@ public class SevenZExtractor implements Extractor, RandomAccess {
/**
* 构造
*
* @param file 包文件
* @param file 包文件
*/
public SevenZExtractor(File file) {
this(file, null);
@ -53,7 +53,7 @@ public class SevenZExtractor implements Extractor, RandomAccess {
/**
* 构造
*
* @param in 包流
* @param in 包流
*/
public SevenZExtractor(InputStream in) {
this(in, null);
@ -72,7 +72,7 @@ public class SevenZExtractor implements Extractor, RandomAccess {
/**
* 构造
*
* @param channel {@link SeekableByteChannel}
* @param channel {@link SeekableByteChannel}
*/
public SevenZExtractor(SeekableByteChannel channel) {
this(channel, null);
@ -99,9 +99,9 @@ public class SevenZExtractor implements Extractor, RandomAccess {
* @param filter 解压文件过滤器用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时释放
*/
@Override
public void extract(File targetDir, Filter<ArchiveEntry> filter) {
public void extract(File targetDir, int stripComponents, Filter<ArchiveEntry> filter) {
try {
extractInternal(targetDir, filter);
extractInternal(targetDir, stripComponents, filter);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
@ -113,16 +113,16 @@ public class SevenZExtractor implements Extractor, RandomAccess {
* 获取满足指定过滤要求的压缩包内的第一个文件流
*
* @param filter 用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时返回对应流
* @return 满足过滤要求的第一个文件的流,无满足条件的文件返回{@code null}
* @return 满足过滤要求的第一个文件的流, 无满足条件的文件返回{@code null}
* @since 5.7.14
*/
public InputStream getFirst(Filter<ArchiveEntry> filter) {
final SevenZFile sevenZFile = this.sevenZFile;
for(SevenZArchiveEntry entry : sevenZFile.getEntries()){
if(null != filter && false == filter.accept(entry)){
for (SevenZArchiveEntry entry : sevenZFile.getEntries()) {
if (null != filter && false == filter.accept(entry)) {
continue;
}
if(entry.isDirectory()){
if (entry.isDirectory()) {
continue;
}
@ -143,32 +143,38 @@ public class SevenZExtractor implements Extractor, RandomAccess {
* @return 文件流无文件返回{@code null}
* @since 5.7.14
*/
public InputStream get(String entryName){
return getFirst((entry)-> StrUtil.equals(entryName, entry.getName()));
public InputStream get(String entryName) {
return getFirst((entry) -> StrUtil.equals(entryName, entry.getName()));
}
/**
* 释放解压到指定目录
*
* @param targetDir 目标目录
* @param filter 解压文件过滤器用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时释放
* @param targetDir 目标目录
* @param stripComponents 清除(剥离)压缩包里面的 n 级文件夹名
* @param filter 解压文件过滤器用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时释放
* @throws IOException IO异常
*/
private void extractInternal(File targetDir, Filter<ArchiveEntry> filter) throws IOException {
private void extractInternal(File targetDir, int stripComponents, Filter<ArchiveEntry> filter) throws IOException {
Assert.isTrue(null != targetDir && ((false == targetDir.exists()) || targetDir.isDirectory()), "target must be dir.");
final SevenZFile sevenZFile = this.sevenZFile;
SevenZArchiveEntry entry;
File outItemFile;
while (null != (entry = this.sevenZFile.getNextEntry())) {
if(null != filter && false == filter.accept(entry)){
if (null != filter && false == filter.accept(entry)) {
continue;
}
outItemFile = FileUtil.file(targetDir, entry.getName());
String entryName = this.stripName(entry.getName(), stripComponents);
if (entryName == null) {
// 剥离文件夹层级
continue;
}
outItemFile = FileUtil.file(targetDir, entryName);
if (entry.isDirectory()) {
// 创建对应目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else if(entry.hasStream()){
} else if (entry.hasStream()) {
// 读取entry对应数据流
FileUtil.writeFromStream(new Seven7EntryInputStream(sevenZFile, entry), outItemFile);
} else {

View File

@ -25,15 +25,15 @@ import java.nio.charset.Charset;
* @author looly
* @since 5.5.0
*/
public class StreamExtractor implements Extractor{
public class StreamExtractor implements Extractor {
private final ArchiveInputStream in;
/**
* 构造
*
* @param charset 编码
* @param file 包文件
* @param charset 编码
* @param file 包文件
*/
public StreamExtractor(Charset charset, File file) {
this(charset, null, file);
@ -70,7 +70,7 @@ public class StreamExtractor implements Extractor{
*/
public StreamExtractor(Charset charset, String archiverName, InputStream in) {
// issue#2736 自定义ArchiveInputStream
if(in instanceof ArchiveInputStream){
if (in instanceof ArchiveInputStream) {
this.in = (ArchiveInputStream) in;
return;
}
@ -80,7 +80,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));
@ -104,9 +104,9 @@ public class StreamExtractor implements Extractor{
* @param filter 解压文件过滤器用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时释放
*/
@Override
public void extract(File targetDir, Filter<ArchiveEntry> filter) {
public void extract(File targetDir, int stripComponents, Filter<ArchiveEntry> filter) {
try {
extractInternal(targetDir, filter);
extractInternal(targetDir, stripComponents, filter);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
@ -117,24 +117,30 @@ public class StreamExtractor implements Extractor{
/**
* 释放解压到指定目录
*
* @param targetDir 目标目录
* @param filter 解压文件过滤器用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时释放
* @param targetDir 目标目录
* @param stripComponents 清除(剥离)压缩包里面的 n 级文件夹名
* @param filter 解压文件过滤器用于指定需要释放的文件null表示不过滤{@link Filter#accept(Object)}为true时释放
* @throws IOException IO异常
*/
private void extractInternal(File targetDir, Filter<ArchiveEntry> filter) throws IOException {
private void extractInternal(File targetDir, int stripComponents, Filter<ArchiveEntry> filter) throws IOException {
Assert.isTrue(null != targetDir && ((false == targetDir.exists()) || targetDir.isDirectory()), "target must be dir.");
final ArchiveInputStream in = this.in;
ArchiveEntry entry;
File outItemFile;
while (null != (entry = in.getNextEntry())) {
if(null != filter && false == filter.accept(entry)){
if (null != filter && false == filter.accept(entry)) {
continue;
}
if (false == in.canReadEntryData(entry)) {
// 无法读取的文件直接跳过
continue;
}
outItemFile = FileUtil.file(targetDir, entry.getName());
String entryName = this.stripName(entry.getName(), stripComponents);
if (entryName == null) {
// 剥离文件夹层级
continue;
}
outItemFile = FileUtil.file(targetDir, entryName);
if (entry.isDirectory()) {
// 创建对应目录
//noinspection ResultOfMethodCallIgnored

View File

@ -6,11 +6,13 @@ import cn.hutool.extra.compress.extractor.Extractor;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
public class ExtractorTest {
@Test
@Ignore
public void zipTest(){
public void zipTest() {
Extractor extractor = CompressUtil.createExtractor(
CharsetUtil.defaultCharset(),
FileUtil.file("d:/test/c_1344112734760931330_20201230104703032.zip"));
@ -20,8 +22,8 @@ public class ExtractorTest {
@Test
@Ignore
public void sevenZTest(){
Extractor extractor = CompressUtil.createExtractor(
public void sevenZTest() {
Extractor extractor = CompressUtil.createExtractor(
CharsetUtil.defaultCharset(),
FileUtil.file("d:/test/compress/test.7z"));
@ -30,12 +32,38 @@ public class ExtractorTest {
@Test
@Ignore
public void tgzTest(){
Extractor extractor = CompressUtil.createExtractor(
public void tgzTest() {
Extractor extractor = CompressUtil.createExtractor(
CharsetUtil.defaultCharset(),
"tgz",
FileUtil.file("d:/test/test.tgz"));
extractor.extract(FileUtil.file("d:/test/tgz/"));
}
@Test
@Ignore
public void sevenZTest2() {
File targetDir = FileUtil.file("d:/test/sevenZ2/");
FileUtil.clean(targetDir);
//
Extractor extractor = CompressUtil.createExtractor(
CharsetUtil.defaultCharset(),
FileUtil.file("D:/System-Data/Downloads/apache-tomcat-10.0.27.7z"));
extractor.extract(targetDir, 1);
}
@Test
@Ignore
public void zipTest2() {
File targetDir = FileUtil.file("d:/test/zip2/");
FileUtil.clean(targetDir);
//
Extractor extractor = CompressUtil.createExtractor(
CharsetUtil.defaultCharset(),
FileUtil.file("D:/System-Data/Downloads/apache-tomcat-10.0.27.zip"));
extractor.extract(targetDir, 1);
}
}