mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix code
This commit is contained in:
parent
470e5415f0
commit
f0fb98c584
@ -12,32 +12,21 @@
|
||||
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.array.ArrayUtil;
|
||||
import cn.hutool.core.classloader.ClassLoaderUtil;
|
||||
import cn.hutool.core.exceptions.UtilException;
|
||||
import cn.hutool.core.lang.func.LambdaInfo;
|
||||
import cn.hutool.core.lang.func.LambdaUtil;
|
||||
import cn.hutool.core.lang.func.SerFunction;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.reflect.FieldUtil;
|
||||
import cn.hutool.core.reflect.MethodUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.array.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.lang.annotation.*;
|
||||
import java.lang.reflect.*;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
@ -70,7 +59,7 @@ public class AnnotationUtil {
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public static Annotation[] getDeclaredAnnotations(final AnnotatedElement element) {
|
||||
return MapUtil.computeIfAbsent(DECLARED_ANNOTATIONS_CACHE, element, AnnotatedElement::getDeclaredAnnotations);
|
||||
return DECLARED_ANNOTATIONS_CACHE.computeIfAbsent(element, AnnotatedElement::getDeclaredAnnotations);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -12,12 +12,11 @@
|
||||
|
||||
package cn.hutool.core.annotation;
|
||||
|
||||
import cn.hutool.core.array.ArrayUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.map.WeakConcurrentMap;
|
||||
import cn.hutool.core.reflect.MethodUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.array.ArrayUtil;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
@ -354,9 +353,7 @@ public interface RepeatableAnnotationCollector {
|
||||
*/
|
||||
@Override
|
||||
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
|
||||
final Object cache = MapUtil.computeIfAbsent(
|
||||
repeatableMethodCache, annotation.annotationType(), this::resolveRepeatableMethodFromType
|
||||
);
|
||||
final Object cache = repeatableMethodCache.computeIfAbsent(annotation.annotationType(), this::resolveRepeatableMethodFromType);
|
||||
return (cache == NONE) ? null : Collections.singletonList((Method)cache);
|
||||
}
|
||||
|
||||
@ -467,9 +464,7 @@ public interface RepeatableAnnotationCollector {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected List<Method> resolveRepeatableMethod(final Annotation annotation) {
|
||||
final Object cache = MapUtil.computeIfAbsent(
|
||||
repeatableMethodCache, annotation.annotationType(), this::resolveRepeatableMethodFromType
|
||||
);
|
||||
final Object cache = repeatableMethodCache.computeIfAbsent(annotation.annotationType(), this::resolveRepeatableMethodFromType);
|
||||
return (cache == NONE) ? null : (List<Method>)cache;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.compress;
|
||||
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* {@link ZipFile} 资源包装
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class ZipFileResource implements ZipResource {
|
||||
|
||||
private final ZipFile zipFile;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param zipFile {@link ZipFile}
|
||||
*/
|
||||
public ZipFileResource(final ZipFile zipFile) {
|
||||
this.zipFile = zipFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(final Consumer<ZipEntry> consumer, final int maxSizeDiff) {
|
||||
final Enumeration<? extends ZipEntry> em = zipFile.entries();
|
||||
while (em.hasMoreElements()) {
|
||||
consumer.accept(ZipSecurityUtil.checkZipBomb(em.nextElement(), maxSizeDiff));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream get(final String path){
|
||||
final ZipFile zipFile = this.zipFile;
|
||||
final ZipEntry entry = zipFile.getEntry(path);
|
||||
if (null != entry) {
|
||||
return ZipUtil.getStream(zipFile, entry);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream get(final ZipEntry entry) {
|
||||
return ZipUtil.getStream(this.zipFile, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
IoUtil.closeQuietly(this.zipFile);
|
||||
}
|
||||
}
|
@ -12,18 +12,15 @@
|
||||
|
||||
package cn.hutool.core.compress;
|
||||
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
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.text.StrUtil;
|
||||
|
||||
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.function.Predicate;
|
||||
import java.util.zip.ZipEntry;
|
||||
@ -39,10 +36,7 @@ import java.util.zip.ZipInputStream;
|
||||
public class ZipReader implements Closeable {
|
||||
|
||||
// size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times
|
||||
private static final int MAX_SIZE_DIFF = 100;
|
||||
|
||||
private ZipFile zipFile;
|
||||
private ZipInputStream in;
|
||||
private static final int DEFAULT_MAX_SIZE_DIFF = 100;
|
||||
|
||||
/**
|
||||
* 创建ZipReader
|
||||
@ -52,7 +46,7 @@ public class ZipReader implements Closeable {
|
||||
* @return ZipReader
|
||||
*/
|
||||
public static ZipReader of(final File zipFile, final Charset charset) {
|
||||
return new ZipReader(zipFile, charset);
|
||||
return new ZipReader(ZipUtil.toZipFile(zipFile, charset));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,18 +57,14 @@ public class ZipReader implements Closeable {
|
||||
* @return ZipReader
|
||||
*/
|
||||
public static ZipReader of(final InputStream in, final Charset charset) {
|
||||
return new ZipReader(in, charset);
|
||||
return new ZipReader(new ZipInputStream(in, charset));
|
||||
}
|
||||
|
||||
private final ZipResource resource;
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param zipFile 读取的的Zip文件
|
||||
* @param charset 编码
|
||||
* 检查ZipBomb文件差异倍数,-1表示不检查ZipBomb
|
||||
*/
|
||||
public ZipReader(final File zipFile, final Charset charset) {
|
||||
this.zipFile = ZipUtil.toZipFile(zipFile, charset);
|
||||
}
|
||||
private int maxSizeDiff = DEFAULT_MAX_SIZE_DIFF;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@ -82,17 +72,7 @@ public class ZipReader implements Closeable {
|
||||
* @param zipFile 读取的的Zip文件
|
||||
*/
|
||||
public ZipReader(final ZipFile zipFile) {
|
||||
this.zipFile = zipFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param in 读取的的Zip文件流
|
||||
* @param charset 编码
|
||||
*/
|
||||
public ZipReader(final InputStream in, final Charset charset) {
|
||||
this.in = new ZipInputStream(in, charset);
|
||||
this(new ZipFileResource(zipFile));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,7 +81,28 @@ public class ZipReader implements Closeable {
|
||||
* @param zin 读取的的Zip文件流
|
||||
*/
|
||||
public ZipReader(final ZipInputStream zin) {
|
||||
this.in = zin;
|
||||
this(new ZipStreamResource(zin));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param resource 读取的的Zip文件流
|
||||
*/
|
||||
public ZipReader(final ZipResource resource) {
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置检查ZipBomb文件差异倍数,-1表示不检查ZipBomb
|
||||
*
|
||||
* @param maxSizeDiff 检查ZipBomb文件差异倍数,-1表示不检查ZipBomb
|
||||
* @return this
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public ZipReader setMaxSizeDiff(final int maxSizeDiff) {
|
||||
this.maxSizeDiff = maxSizeDiff;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -112,27 +113,7 @@ public class ZipReader implements Closeable {
|
||||
* @return 文件流
|
||||
*/
|
||||
public InputStream get(final 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 {
|
||||
try {
|
||||
this.in.reset();
|
||||
ZipEntry zipEntry;
|
||||
while (null != (zipEntry = in.getNextEntry())) {
|
||||
if (zipEntry.getName().equals(path)) {
|
||||
return this.in;
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return this.resource.get(path);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,31 +136,11 @@ public class ZipReader implements Closeable {
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 5.7.12
|
||||
*/
|
||||
@SuppressWarnings("resource")
|
||||
public File readTo(final File outFile, final Predicate<ZipEntry> entryFilter) throws IORuntimeException {
|
||||
read((zipEntry) -> {
|
||||
if (null == entryFilter || entryFilter.test(zipEntry)) {
|
||||
//gitee issue #I4ZDQI
|
||||
String path = zipEntry.getName();
|
||||
if (FileUtil.isWindows()) {
|
||||
// Win系统下
|
||||
path = StrUtil.replace(path, "*", "_");
|
||||
}
|
||||
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
|
||||
final File outItemFile = FileUtil.file(outFile, path);
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
final InputStream in;
|
||||
if (null != this.zipFile) {
|
||||
in = ZipUtil.getStream(this.zipFile, zipEntry);
|
||||
} else {
|
||||
in = this.in;
|
||||
}
|
||||
// 文件
|
||||
FileUtil.writeFromStream(in, outItemFile, false);
|
||||
}
|
||||
readEntry(zipEntry, outFile);
|
||||
}
|
||||
});
|
||||
return outFile;
|
||||
@ -193,70 +154,37 @@ public class ZipReader implements Closeable {
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public ZipReader read(final Consumer<ZipEntry> consumer) throws IORuntimeException {
|
||||
if (null != this.zipFile) {
|
||||
readFromZipFile(consumer);
|
||||
} else {
|
||||
readFromStream(consumer);
|
||||
}
|
||||
resource.read(consumer, this.maxSizeDiff);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IORuntimeException {
|
||||
if (null != this.zipFile) {
|
||||
IoUtil.closeQuietly(this.zipFile);
|
||||
IoUtil.closeQuietly(this.resource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取一个ZipEntry的数据到目标目录下,如果entry是个目录,则创建对应目录,否则解压并写出到文件
|
||||
*
|
||||
* @param zipEntry entry
|
||||
* @param outFile 写出到的目录
|
||||
*/
|
||||
private void readEntry(final ZipEntry zipEntry, final File outFile) {
|
||||
//gitee issue #I4ZDQI
|
||||
String path = zipEntry.getName();
|
||||
if (FileUtil.isWindows()) {
|
||||
// Win系统下
|
||||
path = StrUtil.replace(path, "*", "_");
|
||||
}
|
||||
// FileUtil.file会检查slip漏洞,漏洞说明见http://blog.nsfocus.net/zip-slip-2/
|
||||
final File outItemFile = FileUtil.file(outFile, path);
|
||||
if (zipEntry.isDirectory()) {
|
||||
// 目录
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outItemFile.mkdirs();
|
||||
} else {
|
||||
IoUtil.closeQuietly(this.in);
|
||||
// 文件
|
||||
FileUtil.writeFromStream(this.resource.get(zipEntry), outItemFile, false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取并处理Zip文件中的每一个{@link ZipEntry}
|
||||
*
|
||||
* @param consumer {@link ZipEntry}处理器
|
||||
*/
|
||||
private void readFromZipFile(final Consumer<ZipEntry> consumer) {
|
||||
final Enumeration<? extends ZipEntry> em = zipFile.entries();
|
||||
while (em.hasMoreElements()) {
|
||||
consumer.accept(checkZipBomb(em.nextElement()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取并处理Zip流中的每一个{@link ZipEntry}
|
||||
*
|
||||
* @param consumer {@link ZipEntry}处理器
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
private void readFromStream(final Consumer<ZipEntry> consumer) throws IORuntimeException {
|
||||
try {
|
||||
ZipEntry zipEntry;
|
||||
while (null != (zipEntry = checkZipBomb(in.getNextEntry()))) {
|
||||
consumer.accept(zipEntry);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Zip bomb漏洞
|
||||
*
|
||||
* @param entry {@link ZipEntry}
|
||||
* @return 检查后的{@link ZipEntry}
|
||||
*/
|
||||
private static ZipEntry checkZipBomb(final ZipEntry entry) {
|
||||
if (null == entry) {
|
||||
return null;
|
||||
}
|
||||
final long compressedSize = entry.getCompressedSize();
|
||||
final long uncompressedSize = entry.getSize();
|
||||
if (compressedSize < 0 || uncompressedSize < 0 ||
|
||||
// 默认压缩比例是100倍,一旦发现压缩率超过这个阈值,被认为是Zip bomb
|
||||
compressedSize * MAX_SIZE_DIFF < uncompressedSize) {
|
||||
throw new ValidateException("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
|
||||
compressedSize, uncompressedSize, entry.getName());
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.compress;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
/**
|
||||
* Zip资源表示,如Zip流资源或Zip文件资源
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public interface ZipResource extends Closeable {
|
||||
|
||||
/**
|
||||
* 读取并处理Zip文件中的每一个{@link ZipEntry}
|
||||
*
|
||||
* @param consumer {@link ZipEntry}处理器
|
||||
* @param maxSizeDiff 检查ZipBomb文件差异倍数,-1表示不检查ZipBomb
|
||||
*/
|
||||
void read(final Consumer<ZipEntry> consumer, final int maxSizeDiff);
|
||||
|
||||
/**
|
||||
* 获取指定路径的文件流<br>
|
||||
* 如果是文件模式,则直接获取Entry对应的流,如果是流模式,则遍历entry后,找到对应流返回
|
||||
*
|
||||
* @param path 路径
|
||||
* @return 文件流
|
||||
*/
|
||||
InputStream get(String path);
|
||||
|
||||
/**
|
||||
* 获取指定{@link ZipEntry}对应的文件流
|
||||
*
|
||||
* @param entry @link ZipEntry}
|
||||
* @return 文件流
|
||||
*/
|
||||
InputStream get(ZipEntry entry);
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.compress;
|
||||
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
/**
|
||||
* Zip安全相关工具类,如检查Zip bomb漏洞等
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class ZipSecurityUtil {
|
||||
|
||||
/**
|
||||
* 检查Zip bomb漏洞
|
||||
*
|
||||
* @param entry {@link ZipEntry}
|
||||
* @param maxSizeDiff 检查ZipBomb文件差异倍数,-1表示不检查ZipBomb
|
||||
* @return 检查后的{@link ZipEntry}
|
||||
*/
|
||||
public static ZipEntry checkZipBomb(final ZipEntry entry, final int maxSizeDiff) {
|
||||
if (null == entry) {
|
||||
return null;
|
||||
}
|
||||
if (maxSizeDiff < 0 || entry.isDirectory()) {
|
||||
// 目录不检查
|
||||
return entry;
|
||||
}
|
||||
|
||||
final long compressedSize = entry.getCompressedSize();
|
||||
final long uncompressedSize = entry.getSize();
|
||||
//Console.log(entry.getName(), compressedSize, uncompressedSize);
|
||||
if (compressedSize < 0 || uncompressedSize < 0 ||
|
||||
// 默认压缩比例是100倍,一旦发现压缩率超过这个阈值,被认为是Zip bomb
|
||||
compressedSize * maxSizeDiff < uncompressedSize) {
|
||||
throw new ValidateException("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
|
||||
compressedSize, uncompressedSize, entry.getName());
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.compress;
|
||||
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* {@link ZipInputStream} 资源包装
|
||||
*
|
||||
* @author looly
|
||||
* @since 6.0.0
|
||||
*/
|
||||
public class ZipStreamResource implements ZipResource {
|
||||
|
||||
private final ZipInputStream in;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param in {@link ZipInputStream}
|
||||
*/
|
||||
public ZipStreamResource(final ZipInputStream in) {
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(final Consumer<ZipEntry> consumer, final int maxSizeDiff) {
|
||||
try {
|
||||
ZipEntry zipEntry;
|
||||
while (null != (zipEntry = in.getNextEntry())) {
|
||||
consumer.accept(zipEntry);
|
||||
// 检查ZipBomb放在读取内容之后,以便entry中的信息正常读取
|
||||
ZipSecurityUtil.checkZipBomb(zipEntry, maxSizeDiff);
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream get(final String path) {
|
||||
try {
|
||||
this.in.reset();
|
||||
ZipEntry zipEntry;
|
||||
while (null != (zipEntry = in.getNextEntry())) {
|
||||
if (zipEntry.getName().equals(path)) {
|
||||
return this.in;
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream get(final ZipEntry entry) {
|
||||
return this.in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
IoUtil.closeQuietly(this.in);
|
||||
}
|
||||
}
|
@ -794,7 +794,7 @@ public class ZipUtil {
|
||||
* @return 解压后的字符串
|
||||
* @throws UtilException IO异常
|
||||
*/
|
||||
public static String unGzip(final byte[] buf, final String charset) throws UtilException {
|
||||
public static String unGzip(final byte[] buf, final Charset charset) throws UtilException {
|
||||
return StrUtil.str(unGzip(buf), charset);
|
||||
}
|
||||
|
||||
@ -915,7 +915,7 @@ public class ZipUtil {
|
||||
* @return 解压后的字符串
|
||||
* @since 4.1.4
|
||||
*/
|
||||
public static String unZlib(final byte[] buf, final String charset) {
|
||||
public static String unZlib(final byte[] buf, final Charset charset) {
|
||||
return StrUtil.str(unZlib(buf), charset);
|
||||
}
|
||||
|
||||
|
@ -292,8 +292,9 @@ public class ZipWriter implements Closeable {
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
private ZipWriter putEntry(final String path, final InputStream in) throws IORuntimeException {
|
||||
final ZipEntry entry = new ZipEntry(path);
|
||||
try {
|
||||
out.putNextEntry(new ZipEntry(path));
|
||||
out.putNextEntry(entry);
|
||||
if (null != in) {
|
||||
IoUtil.copy(in, out);
|
||||
}
|
||||
|
@ -204,18 +204,6 @@ public class NioUtil {
|
||||
return read(fileChannel, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从FileChannel中读取内容,读取完毕后并不关闭Channel
|
||||
*
|
||||
* @param fileChannel 文件管道
|
||||
* @param charsetName 字符集
|
||||
* @return 内容
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static String read(final FileChannel fileChannel, final String charsetName) throws IORuntimeException {
|
||||
return read(fileChannel, CharsetUtil.charset(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从FileChannel中读取内容
|
||||
*
|
||||
|
@ -29,7 +29,10 @@ import cn.hutool.core.regex.ReUtil;
|
||||
import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.text.split.SplitUtil;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.SystemUtil;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URI;
|
||||
@ -137,6 +140,7 @@ public class FileUtil extends PathUtil {
|
||||
return isDirEmpty(dir.toPath());
|
||||
}
|
||||
|
||||
// region ----- loop and walk
|
||||
/**
|
||||
* 递归遍历目录以及子目录中的所有文件<br>
|
||||
* 如果提供file为文件,直接返回过滤结果
|
||||
@ -265,6 +269,7 @@ public class FileUtil extends PathUtil {
|
||||
IoUtil.closeQuietly(jarFile);
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region ----- file and newFile
|
||||
/**
|
||||
@ -1707,20 +1712,6 @@ public class FileUtil extends PathUtil {
|
||||
return readLines(path, CharsetUtil.UTF_8, collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
* @param <T> 集合类型
|
||||
* @param path 文件路径
|
||||
* @param charset 字符集
|
||||
* @param collection 集合
|
||||
* @return 文件中的每行内容的集合
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static <T extends Collection<String>> T readLines(final String path, final String charset, final T collection) throws IORuntimeException {
|
||||
return readLines(file(path), charset, collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
@ -1749,20 +1740,6 @@ public class FileUtil extends PathUtil {
|
||||
return readLines(file, CharsetUtil.UTF_8, collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
* @param <T> 集合类型
|
||||
* @param file 文件路径
|
||||
* @param charset 字符集
|
||||
* @param collection 集合
|
||||
* @return 文件中的每行内容的集合
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static <T extends Collection<String>> T readLines(final File file, final String charset, final T collection) throws IORuntimeException {
|
||||
return FileReader.of(file, CharsetUtil.charset(charset)).readLines(collection);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
@ -1848,18 +1825,6 @@ public class FileUtil extends PathUtil {
|
||||
return readLines(path, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
* @param path 文件路径
|
||||
* @param charset 字符集
|
||||
* @return 文件中的每行内容的集合List
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static List<String> readLines(final String path, final String charset) throws IORuntimeException {
|
||||
return readLines(path, charset, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
@ -1885,18 +1850,6 @@ public class FileUtil extends PathUtil {
|
||||
return readLines(file, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
* @param file 文件
|
||||
* @param charset 字符集
|
||||
* @return 文件中的每行内容的集合List
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static List<String> readLines(final File file, final String charset) throws IORuntimeException {
|
||||
return readLines(file, charset, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件中读取每一行数据
|
||||
*
|
||||
@ -2176,19 +2129,6 @@ public class FileUtil extends PathUtil {
|
||||
return writeString(content, file, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,覆盖模式
|
||||
*
|
||||
* @param content 写入的内容
|
||||
* @param path 文件路径
|
||||
* @param charset 字符集
|
||||
* @return 写入的文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static File writeString(final String content, final String path, final String charset) throws IORuntimeException {
|
||||
return writeString(content, touch(path), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,覆盖模式
|
||||
*
|
||||
@ -2202,19 +2142,6 @@ public class FileUtil extends PathUtil {
|
||||
return writeString(content, touch(path), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,覆盖模式
|
||||
*
|
||||
* @param content 写入的内容
|
||||
* @param file 文件
|
||||
* @param charset 字符集
|
||||
* @return 被写入的文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static File writeString(final String content, final File file, final String charset) throws IORuntimeException {
|
||||
return FileWriter.of(file, CharsetUtil.charset(charset)).write(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,覆盖模式
|
||||
*
|
||||
@ -2241,19 +2168,6 @@ public class FileUtil extends PathUtil {
|
||||
return appendString(content, path, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,追加模式
|
||||
*
|
||||
* @param content 写入的内容
|
||||
* @param path 文件路径
|
||||
* @param charset 字符集
|
||||
* @return 写入的文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static File appendString(final String content, final String path, final String charset) throws IORuntimeException {
|
||||
return appendString(content, touch(path), charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,追加模式
|
||||
*
|
||||
@ -2280,19 +2194,6 @@ public class FileUtil extends PathUtil {
|
||||
return appendString(content, file, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,追加模式
|
||||
*
|
||||
* @param content 写入的内容
|
||||
* @param file 文件
|
||||
* @param charset 字符集
|
||||
* @return 写入的文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static File appendString(final String content, final File file, final String charset) throws IORuntimeException {
|
||||
return FileWriter.of(file, CharsetUtil.charset(charset)).append(content);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将String写入文件,追加模式
|
||||
*
|
||||
@ -2334,20 +2235,6 @@ public class FileUtil extends PathUtil {
|
||||
return writeLines(list, file, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,覆盖模式
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 列表
|
||||
* @param path 绝对路径
|
||||
* @param charset 字符集
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static <T> File writeLines(final Collection<T> list, final String path, final String charset) throws IORuntimeException {
|
||||
return writeLines(list, path, charset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,覆盖模式
|
||||
*
|
||||
@ -2362,21 +2249,6 @@ public class FileUtil extends PathUtil {
|
||||
return writeLines(list, path, charset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,覆盖模式
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 列表
|
||||
* @param file 文件
|
||||
* @param charset 字符集
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 4.2.0
|
||||
*/
|
||||
public static <T> File writeLines(final Collection<T> list, final File file, final String charset) throws IORuntimeException {
|
||||
return writeLines(list, file, charset, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,覆盖模式
|
||||
*
|
||||
@ -2420,35 +2292,6 @@ public class FileUtil extends PathUtil {
|
||||
return appendLines(list, path, CharsetUtil.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,追加模式
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 列表
|
||||
* @param path 绝对路径
|
||||
* @param charset 字符集
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static <T> File appendLines(final Collection<T> list, final String path, final String charset) throws IORuntimeException {
|
||||
return writeLines(list, path, charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,追加模式
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 列表
|
||||
* @param file 文件
|
||||
* @param charset 字符集
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
* @since 3.1.2
|
||||
*/
|
||||
public static <T> File appendLines(final Collection<T> list, final File file, final String charset) throws IORuntimeException {
|
||||
return writeLines(list, file, charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件,追加模式
|
||||
*
|
||||
@ -2483,21 +2326,6 @@ public class FileUtil extends PathUtil {
|
||||
return writeLines(list, file, charset, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 列表
|
||||
* @param path 文件路径
|
||||
* @param charset 字符集
|
||||
* @param isAppend 是否追加
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static <T> File writeLines(final Collection<T> list, final String path, final String charset, final boolean isAppend) throws IORuntimeException {
|
||||
return writeLines(list, file(path), charset, isAppend);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件
|
||||
*
|
||||
@ -2513,21 +2341,6 @@ public class FileUtil extends PathUtil {
|
||||
return writeLines(list, file(path), charset, isAppend);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件
|
||||
*
|
||||
* @param <T> 集合元素类型
|
||||
* @param list 列表
|
||||
* @param file 文件
|
||||
* @param charset 字符集
|
||||
* @param isAppend 是否追加
|
||||
* @return 目标文件
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
public static <T> File writeLines(final Collection<T> list, final File file, final String charset, final boolean isAppend) throws IORuntimeException {
|
||||
return FileWriter.of(file, CharsetUtil.charset(charset)).writeLines(list, isAppend);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将列表写入文件
|
||||
*
|
||||
|
@ -74,16 +74,6 @@ public class FileWriter extends FileWrapper {
|
||||
checkFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param file 文件
|
||||
* @param charset 编码,使用 {@link CharsetUtil#charset(String)}
|
||||
*/
|
||||
public FileWriter(final File file, final String charset) {
|
||||
this(file, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
|
@ -1285,8 +1285,12 @@ public class MapUtil extends MapGetUtil {
|
||||
if (JdkUtil.IS_JDK8) {
|
||||
V value = map.get(key);
|
||||
if (null == value) {
|
||||
//map.putIfAbsent(key, mappingFunction.apply(key));
|
||||
value = map.computeIfAbsent(key, mappingFunction);
|
||||
map.putIfAbsent(key, mappingFunction.apply(key));
|
||||
value = map.get(key);
|
||||
|
||||
// 判空后调用依旧无法解决死循环问题
|
||||
// 见:Issue2349Test
|
||||
//value = map.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
|
@ -12,6 +12,8 @@
|
||||
|
||||
package cn.hutool.core.map;
|
||||
|
||||
import cn.hutool.core.util.JdkUtil;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
@ -43,7 +45,7 @@ public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
*
|
||||
* @param initialCapacity 预估初始大小
|
||||
*/
|
||||
public SafeConcurrentHashMap(int initialCapacity) {
|
||||
public SafeConcurrentHashMap(final int initialCapacity) {
|
||||
super(initialCapacity);
|
||||
}
|
||||
|
||||
@ -52,7 +54,7 @@ public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
*
|
||||
* @param m 初始键值对
|
||||
*/
|
||||
public SafeConcurrentHashMap(Map<? extends K, ? extends V> m) {
|
||||
public SafeConcurrentHashMap(final Map<? extends K, ? extends V> m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
@ -62,7 +64,7 @@ public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
* @param initialCapacity 初始容量
|
||||
* @param loadFactor 增长系数
|
||||
*/
|
||||
public SafeConcurrentHashMap(int initialCapacity, float loadFactor) {
|
||||
public SafeConcurrentHashMap(final int initialCapacity, final float loadFactor) {
|
||||
super(initialCapacity, loadFactor);
|
||||
}
|
||||
|
||||
@ -73,14 +75,27 @@ public class SafeConcurrentHashMap<K, V> extends ConcurrentHashMap<K, V> {
|
||||
* @param loadFactor 增长系数
|
||||
* @param concurrencyLevel 并发级别,即Segment的个数
|
||||
*/
|
||||
public SafeConcurrentHashMap(int initialCapacity,
|
||||
float loadFactor, int concurrencyLevel) {
|
||||
public SafeConcurrentHashMap(final int initialCapacity,
|
||||
final float loadFactor, final int concurrencyLevel) {
|
||||
super(initialCapacity, loadFactor, concurrencyLevel);
|
||||
}
|
||||
// endregion == 构造 ==
|
||||
|
||||
@Override
|
||||
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
|
||||
return MapUtil.computeIfAbsent(this, key, mappingFunction);
|
||||
public V computeIfAbsent(final K key, final Function<? super K, ? extends V> mappingFunction) {
|
||||
if (JdkUtil.IS_JDK8) {
|
||||
V value = get(key);
|
||||
if (null == value) {
|
||||
putIfAbsent(key, mappingFunction.apply(key));
|
||||
value = get(key);
|
||||
|
||||
// 判空后调用依旧无法解决死循环问题
|
||||
// 见:Issue2349Test
|
||||
//value = map.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
return value;
|
||||
} else {
|
||||
return super.computeIfAbsent(key, mappingFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -191,17 +191,6 @@ public class UrlQueryUtil {
|
||||
return Convert.toMap(String.class, String.class, queryMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
* @param paramsStr 参数字符串(或者带参数的Path)
|
||||
* @param charset 字符集
|
||||
* @return 参数Map
|
||||
*/
|
||||
public static Map<String, List<String>> decodeQuery(final String paramsStr, final String charset) {
|
||||
return decodeQueryList(paramsStr, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URL参数解析为Map(也可以解析Post中的键值对参数)
|
||||
*
|
||||
|
@ -164,17 +164,6 @@ public class StrUtil extends CharSequenceUtil implements StrPool {
|
||||
return obj.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 将byte数组转为字符串
|
||||
*
|
||||
* @param bytes byte数组
|
||||
* @param charset 字符集
|
||||
* @return 字符串
|
||||
*/
|
||||
public static String str(final byte[] bytes, final String charset) {
|
||||
return str(bytes, CharsetUtil.charset(charset));
|
||||
}
|
||||
|
||||
/**
|
||||
* 解码字节码
|
||||
*
|
||||
|
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.core.compress;
|
||||
|
||||
import cn.hutool.core.io.file.FileUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class Issue3018Test {
|
||||
@Test
|
||||
void unzipTest() {
|
||||
ZipUtil.unzip(FileUtil.getInputStream("d:/test/default.zip"),
|
||||
FileUtil.file("d:/test/"),
|
||||
CharsetUtil.UTF_8
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unzipFromFileTest() {
|
||||
ZipUtil.unzip("d:/test/default.zip");
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ public class UrlQueryUtilTest {
|
||||
@Test
|
||||
public void decodeQueryTest() {
|
||||
final String paramsStr = "uuuu=0&a=b&c=%3F%23%40!%24%25%5E%26%3Ddsssss555555";
|
||||
final Map<String, List<String>> map = UrlQueryUtil.decodeQuery(paramsStr, CharsetUtil.NAME_UTF_8);
|
||||
final Map<String, List<String>> map = UrlQueryUtil.decodeQueryList(paramsStr, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("0", map.get("uuuu").get(0));
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("?#@!$%^&=dsssss555555", map.get("c").get(0));
|
||||
@ -28,7 +28,7 @@ public class UrlQueryUtilTest {
|
||||
@Test
|
||||
public void toQueryTest() {
|
||||
final String paramsStr = "uuuu=0&a=b&c=3Ddsssss555555";
|
||||
final Map<String, List<String>> map = UrlQueryUtil.decodeQuery(paramsStr, CharsetUtil.NAME_UTF_8);
|
||||
final Map<String, List<String>> map = UrlQueryUtil.decodeQueryList(paramsStr, CharsetUtil.UTF_8);
|
||||
|
||||
final String encodedParams = UrlQueryUtil.toQuery(map);
|
||||
Assertions.assertEquals(paramsStr, encodedParams);
|
||||
@ -91,41 +91,41 @@ public class UrlQueryUtilTest {
|
||||
public void decodeParamTest() {
|
||||
// 开头的?被去除
|
||||
String a = "?a=b&c=d&";
|
||||
Map<String, List<String>> map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
Map<String, List<String>> map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("d", map.get("c").get(0));
|
||||
|
||||
// =e被当作空为key,e为value
|
||||
a = "?a=b&c=d&=e";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("d", map.get("c").get(0));
|
||||
Assertions.assertEquals("e", map.get("").get(0));
|
||||
|
||||
// 多余的&去除
|
||||
a = "?a=b&c=d&=e&&&&";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("d", map.get("c").get(0));
|
||||
Assertions.assertEquals("e", map.get("").get(0));
|
||||
|
||||
// 值为空
|
||||
a = "?a=b&c=d&e=";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("d", map.get("c").get(0));
|
||||
Assertions.assertEquals("", map.get("e").get(0));
|
||||
|
||||
// &=被作为键和值都为空
|
||||
a = "a=b&c=d&=";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("d", map.get("c").get(0));
|
||||
Assertions.assertEquals("", map.get("").get(0));
|
||||
|
||||
// &e&这类单独的字符串被当作key
|
||||
a = "a=b&c=d&e&";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("b", map.get("a").get(0));
|
||||
Assertions.assertEquals("d", map.get("c").get(0));
|
||||
Assertions.assertNull(map.get("e").get(0));
|
||||
@ -133,7 +133,7 @@ public class UrlQueryUtilTest {
|
||||
|
||||
// 被编码的键和值被还原
|
||||
a = "a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=";
|
||||
map = UrlQueryUtil.decodeQuery(a, CharsetUtil.NAME_UTF_8);
|
||||
map = UrlQueryUtil.decodeQueryList(a, CharsetUtil.UTF_8);
|
||||
Assertions.assertEquals("bbb", map.get("a").get(0));
|
||||
Assertions.assertEquals("你好", map.get("c").get(0));
|
||||
Assertions.assertEquals("", map.get("哈喽").get(0));
|
||||
|
@ -43,7 +43,6 @@
|
||||
<zxing.version>3.5.1</zxing.version>
|
||||
<net.version>3.9.0</net.version>
|
||||
<emoji-java.version>5.1.1</emoji-java.version>
|
||||
<servlet-api.version>4.0.1</servlet-api.version>
|
||||
<spring-boot.version>2.7.5</spring-boot.version>
|
||||
<cglib.version>3.3.0</cglib.version>
|
||||
</properties>
|
||||
@ -64,21 +63,6 @@
|
||||
<artifactId>hutool-setting</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>${servlet-api.version}</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<!-- 版本固定5.0.0,可以支持jdk8 -->
|
||||
<version>5.0.0</version>
|
||||
<scope>provided</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 模板引擎 -->
|
||||
<dependency>
|
||||
|
@ -50,6 +50,21 @@
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Servlet -->
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<version>4.0.1</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<!-- 版本固定5.0.0,可以支持jdk8 -->
|
||||
<version>5.0.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- 第三方HTTP客户端库 -->
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents.client5</groupId>
|
||||
|
@ -10,7 +10,7 @@
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.extra.servlet;
|
||||
package cn.hutool.http.server.servlet;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
@ -33,6 +33,7 @@ import cn.hutool.core.array.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
|
||||
import cn.hutool.http.meta.Method;
|
||||
import jakarta.servlet.ServletOutputStream;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.http.Cookie;
|
||||
@ -66,14 +67,6 @@ import java.util.Map;
|
||||
*/
|
||||
public class JakartaServletUtil {
|
||||
|
||||
public static final String METHOD_DELETE = "DELETE";
|
||||
public static final String METHOD_HEAD = "HEAD";
|
||||
public static final String METHOD_GET = "GET";
|
||||
public static final String METHOD_OPTIONS = "OPTIONS";
|
||||
public static final String METHOD_POST = "POST";
|
||||
public static final String METHOD_PUT = "PUT";
|
||||
public static final String METHOD_TRACE = "TRACE";
|
||||
|
||||
// --------------------------------------------------------- getParam start
|
||||
|
||||
/**
|
||||
@ -373,18 +366,6 @@ public class JakartaServletUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
* @param request 请求对象{@link HttpServletRequest}
|
||||
* @param name 头信息的KEY
|
||||
* @param charsetName 字符集
|
||||
* @return header值
|
||||
*/
|
||||
public static String getHeader(final HttpServletRequest request, final String name, final String charsetName) {
|
||||
return getHeader(request, name, CharsetUtil.charset(charsetName));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得请求header中的信息
|
||||
*
|
||||
@ -425,7 +406,7 @@ public class JakartaServletUtil {
|
||||
* @return 是否为GET请求
|
||||
*/
|
||||
public static boolean isGetMethod(final HttpServletRequest request) {
|
||||
return METHOD_GET.equalsIgnoreCase(request.getMethod());
|
||||
return Method.GET.name().equalsIgnoreCase(request.getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -435,7 +416,7 @@ public class JakartaServletUtil {
|
||||
* @return 是否为POST请求
|
||||
*/
|
||||
public static boolean isPostMethod(final HttpServletRequest request) {
|
||||
return METHOD_POST.equalsIgnoreCase(request.getMethod());
|
||||
return Method.POST.name().equalsIgnoreCase(request.getMethod());
|
||||
}
|
||||
|
||||
/**
|
@ -10,7 +10,7 @@
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.extra.servlet;
|
||||
package cn.hutool.http.server.servlet;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
@ -32,6 +32,7 @@ import cn.hutool.core.text.StrUtil;
|
||||
import cn.hutool.core.array.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.http.meta.Method;
|
||||
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.ServletRequest;
|
||||
@ -65,14 +66,6 @@ import java.util.Map;
|
||||
*/
|
||||
public class ServletUtil {
|
||||
|
||||
public static final String METHOD_DELETE = "DELETE";
|
||||
public static final String METHOD_HEAD = "HEAD";
|
||||
public static final String METHOD_GET = "GET";
|
||||
public static final String METHOD_OPTIONS = "OPTIONS";
|
||||
public static final String METHOD_POST = "POST";
|
||||
public static final String METHOD_PUT = "PUT";
|
||||
public static final String METHOD_TRACE = "TRACE";
|
||||
|
||||
// --------------------------------------------------------- getParam start
|
||||
|
||||
/**
|
||||
@ -424,7 +417,7 @@ public class ServletUtil {
|
||||
* @return 是否为GET请求
|
||||
*/
|
||||
public static boolean isGetMethod(final HttpServletRequest request) {
|
||||
return METHOD_GET.equalsIgnoreCase(request.getMethod());
|
||||
return Method.GET.name().equalsIgnoreCase(request.getMethod());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -434,7 +427,7 @@ public class ServletUtil {
|
||||
* @return 是否为POST请求
|
||||
*/
|
||||
public static boolean isPostMethod(final HttpServletRequest request) {
|
||||
return METHOD_POST.equalsIgnoreCase(request.getMethod());
|
||||
return Method.POST.name().equalsIgnoreCase(request.getMethod());
|
||||
}
|
||||
|
||||
/**
|
@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Servlet封装,包括Servlet参数获取、文件上传、Response写出等,入口为ServletUtil
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.http.server.servlet;
|
@ -1,4 +1,16 @@
|
||||
package cn.hutool.extra.servlet;
|
||||
/*
|
||||
* Copyright (c) 2023 looly(loolly@aliyun.com)
|
||||
* Hutool is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
* See the Mulan PSL v2 for more details.
|
||||
*/
|
||||
|
||||
package cn.hutool.http.server.servlet;
|
||||
|
||||
import cn.hutool.core.util.ByteUtil;
|
||||
import org.junit.jupiter.api.Disabled;
|
Loading…
x
Reference in New Issue
Block a user