add cpmpress

This commit is contained in:
Looly 2021-08-07 23:32:48 +08:00
parent 5154799ee8
commit 9acc216f90
10 changed files with 760 additions and 317 deletions

View File

@ -8,6 +8,7 @@
### 🐣新特性
* 【core 】 MapProxy支持return this的setter方法pr#392@Gitee
* 【core 】 BeeDSFactory移除sqlite事务修复代码新版本BeeCP已修复
* 【core 】 增加compress包扩充Zip操作灵活性
### 🐞Bug修复

View File

@ -0,0 +1,98 @@
package cn.hutool.core.compress;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
/**
* Deflate算法<br>
* Deflate是同时使用了LZ77算法与哈夫曼编码Huffman Coding的一个无损数据压缩算法
*
* @author looly
* @since 5.7.8
*/
public class Deflate implements Closeable {
private final InputStream source;
private OutputStream target;
private final boolean nowrap;
/**
* 创建Deflate
*
* @param source 源流
* @param target 目标流
* @param nowrap {@code true}表示兼容Gzip压缩
*/
public static Deflate of(InputStream source, OutputStream target, boolean nowrap) {
return new Deflate(source, target, nowrap);
}
/**
* 构造
*
* @param source 源流
* @param target 目标流
* @param nowrap {@code true}表示兼容Gzip压缩
*/
public Deflate(InputStream source, OutputStream target, boolean nowrap) {
this.source = source;
this.target = target;
this.nowrap = nowrap;
}
/**
* 获取目标流
*
* @return 目标流
*/
public OutputStream getTarget() {
return this.target;
}
/**
* 将普通数据流压缩
*
* @param level 压缩级别0~9
*/
public Deflate deflater(int level) {
target= (target instanceof DeflaterOutputStream) ?
(DeflaterOutputStream) target : new DeflaterOutputStream(target, new Deflater(level, nowrap));
IoUtil.copy(source, target);
try {
((DeflaterOutputStream)target).finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
/**
* 将压缩流解压到target中
*/
public Deflate inflater() {
target = (target instanceof InflaterOutputStream) ?
(InflaterOutputStream) target : new InflaterOutputStream(target, new Inflater(nowrap));
IoUtil.copy(source, target);
try {
((InflaterOutputStream)target).finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
@Override
public void close() {
IoUtil.close(this.target);
IoUtil.close(this.source);
}
}

View File

@ -0,0 +1,89 @@
package cn.hutool.core.compress;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
/**
* GZIP是用于Unix系统的文件压缩<br>
* gzip的基础是DEFLATE
*
* @author looly
* @since 5.7.8
*/
public class Gzip implements Closeable {
private InputStream source;
private OutputStream target;
/**
* 创建Gzip
*
* @param source 源流
* @param target 目标流
*/
public static Gzip of(InputStream source, OutputStream target) {
return new Gzip(source, target);
}
/**
* 构造
*
* @param source 源流
* @param target 目标流
*/
public Gzip(InputStream source, OutputStream target) {
this.source = source;
this.target = target;
}
/**
* 获取目标流
*
* @return 目标流
*/
public OutputStream getTarget() {
return this.target;
}
/**
* 将普通数据流压缩
*/
public Gzip gzip() {
try {
target = (target instanceof GZIPOutputStream) ?
(GZIPOutputStream) target : new GZIPOutputStream(target);
IoUtil.copy(source, target);
((GZIPOutputStream)target).finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
/**
* 将压缩流解压到target中
*/
public Gzip unGzip() {
try {
source = (source instanceof GZIPInputStream) ?
(GZIPInputStream) source : new GZIPInputStream(source);
IoUtil.copy(source, target);
} catch (IOException e) {
throw new IORuntimeException(e);
}
return this;
}
@Override
public void close() {
IoUtil.close(this.target);
IoUtil.close(this.source);
}
}

View File

@ -0,0 +1,189 @@
package cn.hutool.core.compress;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ZipUtil;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.function.Consumer;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
/**
* Zip文件或流读取器一般用于Zip文件解压
*
* @author looly
* @since 5.7.8
*/
public class ZipReader implements Closeable {
private ZipFile zipFile;
private ZipInputStream in;
/**
* 创建{@link ZipReader}
*
* @param zipFile 生成的Zip文件
* @param charset 编码
* @return {@link ZipReader}
*/
public static ZipReader of(File zipFile, Charset charset) {
return new ZipReader(zipFile, charset);
}
/**
* 创建{@link ZipReader}
*
* @param in Zip输入的流一般为输入文件流
* @param charset 编码
* @return {@link ZipReader}
*/
public static ZipReader of(InputStream in, Charset charset) {
return new ZipReader(in, charset);
}
/**
* 构造
*
* @param zipFile 读取的的Zip文件
*/
public ZipReader(File zipFile, Charset charset) {
this.zipFile = ZipUtil.toZipFile(zipFile, charset);
}
/**
* 构造
*
* @param zipFile 读取的的Zip文件
*/
public ZipReader(ZipFile zipFile) {
this.zipFile = zipFile;
}
/**
* 构造
*
* @param in 读取的的Zip文件流
*/
public ZipReader(InputStream in, Charset charset) {
this.in = new ZipInputStream(in, charset);
}
/**
* 构造
*
* @param zin 读取的的Zip文件流
*/
public ZipReader(ZipInputStream zin) {
this.in = zin;
}
/**
* 获取指定路径的文件流
*
* @param path 路径
* @return 文件流
*/
public InputStream get(String path) {
if (null != this.zipFile) {
final ZipFile zipFile = this.zipFile;
final ZipEntry entry = zipFile.getEntry(path);
if (null != entry) {
return ZipUtil.getStream(zipFile, entry);
}
} else {
throw new UnsupportedOperationException("Zip stream mode not support get!");
}
return null;
}
/**
* 解压到指定目录中
*
* @param outFile 解压到的目录
* @return 解压的目录
* @throws IORuntimeException IO异常
*/
public File readTo(File outFile) throws IORuntimeException {
read((zipEntry) -> {
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
InputStream in;
if (null != this.zipFile) {
in = ZipUtil.getStream(this.zipFile, zipEntry);
} else {
in = this.in;
}
// 文件
FileUtil.writeFromStream(in, outItemFile, false);
}
});
return outFile;
}
/**
* 读取并处理Zip文件中的每一个{@link ZipEntry}
*
* @param consumer {@link ZipEntry}处理器
* @throws IORuntimeException IO异常
*/
public ZipReader read(Consumer<ZipEntry> consumer) throws IORuntimeException {
if (null != this.zipFile) {
readFromZipFile(consumer);
} else {
readFromStream(consumer);
}
return this;
}
@Override
public void close() throws IORuntimeException {
if(null != this.zipFile){
IoUtil.close(this.zipFile);
} else {
IoUtil.close(this.in);
}
}
/**
* 读取并处理Zip文件中的每一个{@link ZipEntry}
*
* @param consumer {@link ZipEntry}处理器
*/
private void readFromZipFile(Consumer<ZipEntry> consumer) {
final Enumeration<? extends ZipEntry> em = zipFile.entries();
while (em.hasMoreElements()) {
consumer.accept(em.nextElement());
}
}
/**
* 读取并处理Zip流中的每一个{@link ZipEntry}
*
* @param consumer {@link ZipEntry}处理器
* @throws IORuntimeException IO异常
*/
private void readFromStream(Consumer<ZipEntry> consumer) throws IORuntimeException {
try {
ZipEntry zipEntry;
while (null != (zipEntry = in.getNextEntry())) {
consumer.accept(zipEntry);
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
}

View File

@ -0,0 +1,270 @@
package cn.hutool.core.compress;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import java.io.Closeable;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
/**
* Zip生成封装
*
* @author looly
* @since 5.7.8
*/
public class ZipWriter implements Closeable {
/**
* 创建{@link ZipWriter}
*
* @param zipFile 生成的Zip文件
* @param charset 编码
* @return {@link ZipWriter}
*/
public static ZipWriter of(File zipFile, Charset charset) {
return new ZipWriter(zipFile, charset);
}
/**
* 创建{@link ZipWriter}
*
* @param out Zip输出的流一般为输出文件流
* @param charset 编码
* @return {@link ZipWriter}
*/
public static ZipWriter of(OutputStream out, Charset charset) {
return new ZipWriter(out, charset);
}
private final ZipOutputStream out;
/**
* 构造
*
* @param zipFile 生成的Zip文件
*/
public ZipWriter(File zipFile, Charset charset) {
this.out = getZipOutputStream(zipFile, charset);
}
/**
* 构造
*
* @param out {@link ZipOutputStream}
*/
public ZipWriter(OutputStream out, Charset charset) {
this.out = getZipOutputStream(out, charset);
}
/**
* 构造
*
* @param out {@link ZipOutputStream}
*/
public ZipWriter(ZipOutputStream out) {
this.out = out;
}
/**
* 设置压缩级别可选1~9-1表示默认
*
* @param level 压缩级别
* @return this
*/
public ZipWriter setLevel(int level) {
this.out.setLevel(level);
return this;
}
/**
* 设置注释
*
* @param comment 注释
* @return this
*/
public ZipWriter setComment(String comment) {
this.out.setComment(comment);
return this;
}
/**
* 获取原始的{@link ZipOutputStream}
*
* @return {@link ZipOutputStream}
*/
public ZipOutputStream getOut() {
return this.out;
}
/**
* 对文件或文件目录进行压缩
*
* @param withSrcDir 是否包含被打包目录只针对压缩目录有效若为false则只压缩目录下的文件或目录为true则将本目录也压缩
* @param filter 文件过滤器通过实现此接口自定义要过滤的文件过滤掉哪些文件或文件夹不加入压缩{@code null}表示不过滤
* @param files 要压缩的源文件或目录如果压缩一个文件则为该文件的全路径如果压缩一个目录则为该目录的顶层目录路径
* @throws IORuntimeException IO异常
* @since 5.1.1
*/
public ZipWriter add(boolean withSrcDir, FileFilter filter, File... files) throws IORuntimeException {
for (File file : files) {
// 如果只是压缩一个文件则需要截取该文件的父目录
String srcRootDir;
try {
srcRootDir = file.getCanonicalPath();
if ((false == file.isDirectory()) || withSrcDir) {
// 若是文件则将父目录完整路径都截取掉若设置包含目录则将上级目录全部截取掉保留本目录名
srcRootDir = file.getCanonicalFile().getParentFile().getCanonicalPath();
}
} catch (IOException e) {
throw new IORuntimeException(e);
}
_add(file, srcRootDir, filter);
}
return this;
}
/**
* 添加资源到压缩包添加后关闭资源流
*
* @param resources 需要压缩的资源资源的路径为{@link Resource#getName()}
* @throws IORuntimeException IO异常
*/
public ZipWriter add(Resource... resources) throws IORuntimeException {
for (Resource resource : resources) {
if (null != resource) {
add(resource.getName(), resource.getStream());
}
}
return this;
}
/**
* 添加文件流到压缩包添加后关闭输入文件流<br>
* 如果输入流为{@code null}则只创建空目录
*
* @param path 压缩的路径, {@code null}""表示根目录下
* @param in 需要压缩的输入流使用完后自动关闭{@code null}表示加入空目录
* @throws IORuntimeException IO异常
*/
public ZipWriter add(String path, InputStream in) throws IORuntimeException {
path = StrUtil.nullToEmpty(path);
if (null == in) {
// 空目录需要检查路径规范性目录以"/"结尾
path = StrUtil.addSuffixIfNot(path, StrUtil.SLASH);
if (StrUtil.isBlank(path)) {
return this;
}
}
return putEntry(path, in);
}
@Override
public void close() throws IORuntimeException {
try {
out.finish();
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
IoUtil.close(this.out);
}
}
/**
* 获得 {@link ZipOutputStream}
*
* @param zipFile 压缩文件
* @param charset 编码
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(File zipFile, Charset charset) {
return getZipOutputStream(FileUtil.getOutputStream(zipFile), charset);
}
/**
* 获得 {@link ZipOutputStream}
*
* @param out 压缩文件流
* @param charset 编码
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(OutputStream out, Charset charset) {
if (out instanceof ZipOutputStream) {
return (ZipOutputStream) out;
}
return new ZipOutputStream(out, charset);
}
/**
* 递归压缩文件夹或压缩文件<br>
* srcRootDir决定了路径截取的位置例如<br>
* file的路径为d:/a/b/c/d.txtsrcRootDir为d:/a/b则压缩后的文件与目录为结构为c/d.txt
*
* @param srcRootDir 被压缩的文件夹根目录
* @param file 当前递归压缩的文件或目录对象
* @param filter 文件过滤器通过实现此接口自定义要过滤的文件过滤掉哪些文件或文件夹不加入压缩{@code null}表示不过滤
* @throws IORuntimeException IO异常
*/
private ZipWriter _add(File file, String srcRootDir, FileFilter filter) throws IORuntimeException {
if (null == file || (null != filter && false == filter.accept(file))) {
return this;
}
// 获取文件相对于压缩文件夹根目录的子路径
final String subPath = FileUtil.subPath(srcRootDir, file);
if (file.isDirectory()) {
// 如果是目录则压缩压缩目录中的文件或子目录
final File[] files = file.listFiles();
if (ArrayUtil.isEmpty(files)) {
// 加入目录只有空目录时才加入目录非空时会在创建文件时自动添加父级目录
add(subPath, null);
} else {
// 压缩目录下的子文件或目录
for (File childFile : files) {
_add(childFile, srcRootDir, filter);
}
}
} else {
// 如果是文件或其它符号则直接压缩该文件
putEntry(subPath, FileUtil.getInputStream(file));
}
return this;
}
/**
* 添加文件流到压缩包添加后关闭输入文件流<br>
* 如果输入流为{@code null}则只创建空目录
*
* @param path 压缩的路径, {@code null}""表示根目录下
* @param in 需要压缩的输入流使用完后自动关闭{@code null}表示加入空目录
* @throws IORuntimeException IO异常
*/
private ZipWriter putEntry(String path, InputStream in) throws IORuntimeException {
try {
out.putNextEntry(new ZipEntry(path));
if (null != in) {
IoUtil.copy(in, out);
}
out.closeEntry();
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
IoUtil.close(in);
}
IoUtil.flush(this.out);
return this;
}
}

View File

@ -0,0 +1,7 @@
/**
* 压缩解压封装
*
* @author looly
* @since 5.7.8
*/
package cn.hutool.core.compress;

View File

@ -1,5 +1,9 @@
package cn.hutool.core.util;
import cn.hutool.core.compress.Deflate;
import cn.hutool.core.compress.Gzip;
import cn.hutool.core.compress.ZipReader;
import cn.hutool.core.compress.ZipWriter;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.io.FastByteArrayOutputStream;
import cn.hutool.core.io.FileUtil;
@ -18,15 +22,8 @@ import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.function.Consumer;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
@ -35,6 +32,7 @@ import java.util.zip.ZipOutputStream;
/**
* 压缩工具类
*
* @see cn.hutool.core.compress.ZipWriter
* @author Looly
*/
public class ZipUtil {
@ -210,13 +208,7 @@ public class ZipUtil {
*/
public static File zip(File zipFile, Charset charset, boolean withSrcDir, FileFilter filter, File... srcFiles) throws IORuntimeException {
validateFiles(zipFile, srcFiles);
try (ZipOutputStream out = getZipOutputStream(zipFile, charset)) {
zip(out, charset, withSrcDir, filter, srcFiles);
} catch (IOException e) {
throw new IORuntimeException(e);
}
ZipWriter.of(zipFile, charset).add(withSrcDir, filter, srcFiles).close();
return zipFile;
}
@ -232,7 +224,7 @@ public class ZipUtil {
* @since 5.1.1
*/
public static void zip(OutputStream out, Charset charset, boolean withSrcDir, FileFilter filter, File... srcFiles) throws IORuntimeException {
zip(getZipOutputStream(out, charset), withSrcDir, filter, srcFiles);
ZipWriter.of(out, charset).add(withSrcDir, filter, srcFiles).close();
}
/**
@ -244,27 +236,12 @@ public class ZipUtil {
* @param srcFiles 要压缩的源文件或目录如果压缩一个文件则为该文件的全路径如果压缩一个目录则为该目录的顶层目录路径
* @throws IORuntimeException IO异常
* @since 5.1.1
* @deprecated 请使用 {@link #zip(OutputStream, Charset, boolean, FileFilter, File...)}
*/
@Deprecated
public static void zip(ZipOutputStream zipOutputStream, boolean withSrcDir, FileFilter filter, File... srcFiles) throws IORuntimeException {
String srcRootDir;
try {
for (File srcFile : srcFiles) {
if (null == srcFile) {
continue;
}
// 如果只是压缩一个文件则需要截取该文件的父目录
srcRootDir = srcFile.getCanonicalPath();
if (srcFile.isFile() || withSrcDir) {
// 若是文件则将父目录完整路径都截取掉若设置包含目录则将上级目录全部截取掉保留本目录名
srcRootDir = srcFile.getCanonicalFile().getParentFile().getCanonicalPath();
}
// 调用递归压缩方法进行目录或文件压缩
zip(srcFile, srcRootDir, zipOutputStream, filter);
zipOutputStream.flush();
}
zipOutputStream.finish();
} catch (IOException e) {
throw new IORuntimeException(e);
try(final ZipWriter zipWriter = new ZipWriter(zipOutputStream)){
zipWriter.add(withSrcDir, filter, srcFiles);
}
}
@ -355,13 +332,19 @@ public class ZipUtil {
* @since 3.0.9
*/
public static File zip(File zipFile, String[] paths, InputStream[] ins, Charset charset) throws UtilException {
ZipOutputStream out = null;
try {
out = getZipOutputStream(zipFile, charset);
zip(out, paths, ins);
} finally {
IoUtil.close(out);
if (ArrayUtil.isEmpty(paths) || ArrayUtil.isEmpty(ins)) {
throw new IllegalArgumentException("Paths or ins is empty !");
}
if (paths.length != ins.length) {
throw new IllegalArgumentException("Paths length is not equals to ins length !");
}
try(final ZipWriter zipWriter = ZipWriter.of(zipFile, charset)){
for (int i = 0; i < paths.length; i++) {
zipWriter.add(paths[i], ins[i]);
}
}
return zipFile;
}
@ -374,12 +357,17 @@ public class ZipUtil {
* @since 5.5.2
*/
public static void zip(OutputStream out, String[] paths, InputStream[] ins) {
ZipOutputStream zipOutputStream = null;
try {
zipOutputStream = getZipOutputStream(out, DEFAULT_CHARSET);
zip(zipOutputStream, paths, ins);
} finally {
IoUtil.close(zipOutputStream);
if (ArrayUtil.isEmpty(paths) || ArrayUtil.isEmpty(ins)) {
throw new IllegalArgumentException("Paths or ins is empty !");
}
if (paths.length != ins.length) {
throw new IllegalArgumentException("Paths length is not equals to ins length !");
}
try(final ZipWriter zipWriter = ZipWriter.of(out, DEFAULT_CHARSET)){
for (int i = 0; i < paths.length; i++) {
zipWriter.add(paths[i], ins[i]);
}
}
}
@ -399,8 +387,11 @@ public class ZipUtil {
if (paths.length != ins.length) {
throw new IllegalArgumentException("Paths length is not equals to ins length !");
}
for (int i = 0; i < paths.length; i++) {
add(ins[i], paths[i], zipOutputStream);
try(final ZipWriter zipWriter = new ZipWriter(zipOutputStream)){
for (int i = 0; i < paths.length; i++) {
zipWriter.add(paths[i], ins[i]);
}
}
}
@ -416,15 +407,7 @@ public class ZipUtil {
* @since 5.5.2
*/
public static File zip(File zipFile, Charset charset, Resource... resources) throws UtilException {
ZipOutputStream out = null;
try {
out = getZipOutputStream(zipFile, charset);
for (Resource resource : resources) {
add(resource.getStream(), resource.getName(), out);
}
} finally {
IoUtil.close(out);
}
ZipWriter.of(zipFile, charset).add(resources).close();
return zipFile;
}
@ -541,21 +524,13 @@ public class ZipUtil {
*/
public static File unzip(ZipFile zipFile, File outFile) throws IORuntimeException {
if (outFile.exists() && outFile.isFile()) {
throw new UtilException("Target path [{}] exist!", outFile.getAbsolutePath());
throw new IllegalArgumentException(
StrUtil.format("Target path [{}] exist!", outFile.getAbsolutePath()));
}
read(zipFile, (zipEntry) -> {
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 创建对应目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
// 写出文件
write(zipFile, zipEntry, outItemFile);
}
});
try(final ZipReader reader = new ZipReader(zipFile)){
reader.readTo(outFile);
}
return outFile;
}
@ -596,13 +571,8 @@ public class ZipUtil {
* @since 5.5.2
*/
public static void read(ZipFile zipFile, Consumer<ZipEntry> consumer) {
try {
final Enumeration<? extends ZipEntry> em = zipFile.entries();
while (em.hasMoreElements()) {
consumer.accept(em.nextElement());
}
} finally {
IoUtil.close(zipFile);
try(final ZipReader reader = new ZipReader(zipFile)){
reader.read(consumer);
}
}
@ -635,18 +605,9 @@ public class ZipUtil {
* @since 4.5.8
*/
public static File unzip(ZipInputStream zipStream, File outFile) throws UtilException {
read(zipStream, (zipEntry) -> {
// FileUtil.file会检查slip漏洞漏洞说明见http://blog.nsfocus.net/zip-slip-2/
File outItemFile = FileUtil.file(outFile, zipEntry.getName());
if (zipEntry.isDirectory()) {
// 目录
//noinspection ResultOfMethodCallIgnored
outItemFile.mkdirs();
} else {
// 文件
FileUtil.writeFromStream(zipStream, outItemFile, false);
}
});
try(final ZipReader reader = new ZipReader(zipStream)){
reader.readTo(outFile);
}
return outFile;
}
@ -658,15 +619,8 @@ public class ZipUtil {
* @since 5.5.2
*/
public static void read(ZipInputStream zipStream, Consumer<ZipEntry> consumer) {
try {
ZipEntry zipEntry;
while (null != (zipEntry = zipStream.getNextEntry())) {
consumer.accept(zipEntry);
}
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(zipStream);
try(final ZipReader reader = new ZipReader(zipStream)){
reader.read(consumer);
}
}
@ -716,23 +670,10 @@ public class ZipUtil {
* @return 文件内容bytes
* @since 4.1.8
*/
@SuppressWarnings("unchecked")
public static byte[] unzipFileBytes(File zipFile, Charset charset, String name) {
ZipFile zipFileObj = null;
try {
zipFileObj = toZipFile(zipFile, charset);
final Enumeration<ZipEntry> em = (Enumeration<ZipEntry>) zipFileObj.entries();
ZipEntry zipEntry;
while (em.hasMoreElements()) {
zipEntry = em.nextElement();
if ((false == zipEntry.isDirectory()) && name.equals(zipEntry.getName())) {
return IoUtil.readBytes(getStream(zipFileObj, zipEntry));
}
}
} finally {
IoUtil.close(zipFileObj);
try(final ZipReader reader = ZipReader.of(zipFile, charset)){
return IoUtil.readBytes(reader.get(name));
}
return null;
}
// ----------------------------------------------------------------------------- Gzip
@ -800,16 +741,7 @@ public class ZipUtil {
*/
public static byte[] gzip(InputStream in, int length) throws UtilException {
final ByteArrayOutputStream bos = new ByteArrayOutputStream(length);
GZIPOutputStream gos = null;
try {
gos = new GZIPOutputStream(bos);
IoUtil.copy(in, gos);
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(gos);
}
// 返回必须在关闭gos后进行因为关闭时会自动执行finish()方法保证数据全部写出
Gzip.of(in, bos).gzip().close();
return bos.toByteArray();
}
@ -857,18 +789,8 @@ public class ZipUtil {
* @since 4.1.18
*/
public static byte[] unGzip(InputStream in, int length) throws UtilException {
GZIPInputStream gzi = null;
FastByteArrayOutputStream bos;
try {
gzi = (in instanceof GZIPInputStream) ? (GZIPInputStream) in : new GZIPInputStream(in);
bos = new FastByteArrayOutputStream(length);
IoUtil.copy(gzi, bos);
} catch (IOException e) {
throw new UtilException(e);
} finally {
IoUtil.close(gzi);
}
// 返回必须在关闭gos后进行因为关闭时会自动执行finish()方法保证数据全部写出
FastByteArrayOutputStream bos = new FastByteArrayOutputStream(length);
Gzip.of(in, bos).unGzip().close();
return bos.toByteArray();
}
@ -940,7 +862,7 @@ public class ZipUtil {
*/
public static byte[] zlib(InputStream in, int level, int length) {
final ByteArrayOutputStream out = new ByteArrayOutputStream(length);
deflater(in, out, level, false);
Deflate.of(in, out, false).deflater(level);
return out.toByteArray();
}
@ -988,7 +910,7 @@ public class ZipUtil {
*/
public static byte[] unZlib(InputStream in, int length) {
final ByteArrayOutputStream out = new ByteArrayOutputStream(length);
inflater(in, out, false);
Deflate.of(in, out, false).inflater();
return out.toByteArray();
}
@ -1023,123 +945,6 @@ public class ZipUtil {
// ---------------------------------------------------------------------------------------------- Private method start
/**
* 获得 {@link ZipOutputStream}
*
* @param zipFile 压缩文件
* @param charset 编码
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(File zipFile, Charset charset) {
return getZipOutputStream(FileUtil.getOutputStream(zipFile), charset);
}
/**
* 获得 {@link ZipOutputStream}
*
* @param out 压缩文件流
* @param charset 编码
* @return {@link ZipOutputStream}
*/
private static ZipOutputStream getZipOutputStream(OutputStream out, Charset charset) {
if (out instanceof ZipOutputStream) {
return (ZipOutputStream) out;
}
return new ZipOutputStream(out, ObjectUtil.defaultIfNull(charset, DEFAULT_CHARSET));
}
/**
* 递归压缩文件夹<br>
* srcRootDir决定了路径截取的位置例如<br>
* file的路径为d:/a/b/c/d.txtsrcRootDir为d:/a/b则压缩后的文件与目录为结构为c/d.txt
*
* @param out 压缩文件存储对象
* @param srcRootDir 被压缩的文件夹根目录
* @param file 当前递归压缩的文件或目录对象
* @param filter 文件过滤器通过实现此接口自定义要过滤的文件过滤掉哪些文件或文件夹不加入压缩
* @throws UtilException IO异常
*/
private static void zip(File file, String srcRootDir, ZipOutputStream out, FileFilter filter) throws UtilException {
if (null == file || (null != filter && false == filter.accept(file))) {
return;
}
final String subPath = FileUtil.subPath(srcRootDir, file); // 获取文件相对于压缩文件夹根目录的子路径
if (file.isDirectory()) {// 如果是目录则压缩压缩目录中的文件或子目录
final File[] files = file.listFiles();
if (ArrayUtil.isEmpty(files)) {
// 加入目录只有空目录时才加入目录非空时会在创建文件时自动添加父级目录
addDir(subPath, out);
} else{
// 压缩目录下的子文件或目录
for (File childFile : files) {
zip(childFile, srcRootDir, out, filter);
}
}
} else {// 如果是文件或其它符号则直接压缩该文件
add(file, subPath, out);
}
}
/**
* 添加文件到压缩包
*
* @param file 需要压缩的文件
* @param path 在压缩文件中的路径
* @param out 压缩文件存储对象
* @throws IORuntimeException IO异常
* @since 4.0.5
*/
private static void add(File file, String path, ZipOutputStream out) throws IORuntimeException {
add(FileUtil.getInputStream(file), path, out);
}
/**
* 添加文件流到压缩包添加后关闭流
*
* @param in 需要压缩的输入流使用完后自动关闭
* @param path 压缩的路径
* @param out 压缩文件存储对象
* @throws IORuntimeException IO异常
*/
private static void add(InputStream in, String path, ZipOutputStream out) throws IORuntimeException {
if (null == in) {
return;
}
try {
out.putNextEntry(new ZipEntry(path));
IoUtil.copy(in, out);
} catch (IOException e) {
throw new IORuntimeException(e);
} finally {
IoUtil.close(in);
closeEntry(out);
}
}
/**
* 在压缩包中新建目录
*
* @param path 压缩的路径
* @param out 压缩文件存储对象
* @throws UtilException IO异常
*/
private static void addDir(String path, ZipOutputStream out) throws UtilException {
if(StrUtil.isEmpty(path)){
// 空路径不处理
return;
}
path = StrUtil.addSuffixIfNot(path, StrUtil.SLASH);
try {
out.putNextEntry(new ZipEntry(path));
} catch (IOException e) {
throw new UtilException(e);
} finally {
closeEntry(out);
}
}
/**
* 判断压缩文件保存的路径是否为源文件路径的子文件夹如果是则抛出异常防止无限递归压缩的发生
*
@ -1166,19 +971,6 @@ public class ZipUtil {
}
}
/**
* 关闭当前Entry继续下一个Entry
*
* @param out ZipOutputStream
*/
private static void closeEntry(ZipOutputStream out) {
try {
out.closeEntry();
} catch (IOException e) {
// ignore
}
}
/**
* 从Zip中读取文件流并写出到文件
*
@ -1190,43 +982,6 @@ public class ZipUtil {
private static void write(ZipFile zipFile, ZipEntry zipEntry, File outItemFile) throws IORuntimeException {
FileUtil.writeFromStream(getStream(zipFile, zipEntry), outItemFile);
}
/**
* 将Zlib流解压到out中
*
* @param in zlib数据流
* @param out 输出
* @param nowrap true表示兼容Gzip压缩
*/
@SuppressWarnings("SameParameterValue")
private static void inflater(InputStream in, OutputStream out, boolean nowrap) {
final InflaterOutputStream ios = (out instanceof InflaterOutputStream) ? (InflaterOutputStream) out : new InflaterOutputStream(out, new Inflater(nowrap));
IoUtil.copy(in, ios);
try {
ios.finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
/**
* 将普通数据流压缩成zlib到out中
*
* @param in zlib数据流
* @param out 输出
* @param level 压缩级别0~9
* @param nowrap true表示兼容Gzip压缩
*/
@SuppressWarnings("SameParameterValue")
private static void deflater(InputStream in, OutputStream out, int level, boolean nowrap) {
final DeflaterOutputStream ios = (out instanceof DeflaterOutputStream) ? (DeflaterOutputStream) out : new DeflaterOutputStream(out, new Deflater(level, nowrap));
IoUtil.copy(in, ios);
try {
ios.finish();
} catch (IOException e) {
throw new IORuntimeException(e);
}
}
// ---------------------------------------------------------------------------------------------- Private method end
}

View File

@ -0,0 +1,18 @@
package cn.hutool.core.compress;
import cn.hutool.core.lang.Console;
import cn.hutool.core.util.ZipUtil;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
public class ZipReaderTest {
@Test
@Ignore
public void unzipTest() {
File unzip = ZipUtil.unzip("d:/java.zip", "d:/test/java");
Console.log(unzip);
}
}

View File

@ -0,0 +1,16 @@
package cn.hutool.core.compress;
import cn.hutool.core.util.ZipUtil;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
public class ZipWriterTest {
@Test
@Ignore
public void zipDirTest() {
ZipUtil.zip(new File("d:/test"));
}
}

View File

@ -19,21 +19,21 @@ import java.nio.charset.Charset;
*
*/
public class ZipUtilTest {
@Test
@Ignore
public void zipDirTest() {
ZipUtil.zip(new File("e:/picTest/picSubTest"));
ZipUtil.zip(new File("d:/test"));
}
@Test
@Ignore
public void unzipTest() {
File unzip = ZipUtil.unzip("f:/test/apache-maven-3.6.2.zip", "f:\\test");
Console.log(unzip);
}
@Test
@Ignore
public void unzipTest2() {
@ -47,46 +47,46 @@ public class ZipUtilTest {
File unzip = ZipUtil.unzip(FileUtil.getInputStream("e:/test/hutool-core-5.1.0.jar"), FileUtil.file("e:/test/"), CharsetUtil.CHARSET_UTF_8);
Console.log(unzip);
}
@Test
@Ignore
public void unzipChineseTest() {
ZipUtil.unzip("d:/测试.zip");
}
@Test
@Ignore
public void unzipFileBytesTest() {
byte[] fileBytes = ZipUtil.unzipFileBytes(FileUtil.file("e:/02 电力相关设备及服务2-241-.zip"), CharsetUtil.CHARSET_GBK, "images/CE-EP-HY-MH01-ES-0001.jpg");
Assert.assertNotNull(fileBytes);
}
@Test
public void gzipTest() {
String data = "我是一个需要压缩的很长很长的字符串";
byte[] bytes = StrUtil.utf8Bytes(data);
byte[] gzip = ZipUtil.gzip(bytes);
//保证gzip长度正常
Assert.assertEquals(68, gzip.length);
byte[] unGzip = ZipUtil.unGzip(gzip);
//保证正常还原
Assert.assertEquals(data, StrUtil.utf8Str(unGzip));
}
@Test
public void zlibTest() {
String data = "我是一个需要压缩的很长很长的字符串";
byte[] bytes = StrUtil.utf8Bytes(data);
byte[] gzip = ZipUtil.zlib(bytes, 0);
//保证zlib长度正常
Assert.assertEquals(62, gzip.length);
byte[] unGzip = ZipUtil.unZlib(gzip);
//保证正常还原
Assert.assertEquals(data, StrUtil.utf8Str(unGzip));
gzip = ZipUtil.zlib(bytes, 9);
//保证zlib长度正常
Assert.assertEquals(56, gzip.length);