Browse Source

prepare 5.3.0

Looly 5 years ago
parent
commit
6b13cb5263
53 changed files with 617 additions and 329 deletions
  1. 3 0
      CHANGELOG.md
  2. 5 5
      README.md
  3. 1 1
      bin/version.txt
  4. 1 1
      docs/js/version.js
  5. 1 1
      hutool-all/pom.xml
  6. 1 1
      hutool-aop/pom.xml
  7. 1 1
      hutool-bloomFilter/pom.xml
  8. 1 1
      hutool-bom/pom.xml
  9. 1 1
      hutool-cache/pom.xml
  10. 1 1
      hutool-captcha/pom.xml
  11. 1 1
      hutool-core/pom.xml
  12. 7 7
      hutool-core/src/main/java/cn/hutool/core/codec/Base64.java
  13. 71 0
      hutool-core/src/main/java/cn/hutool/core/map/BiMap.java
  14. 20 0
      hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java
  15. 13 11
      hutool-core/src/main/java/cn/hutool/core/map/TableMap.java
  16. 6 6
      hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java
  17. 2 2
      hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java
  18. 2 2
      hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java
  19. 7 18
      hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/MultipartFormData.java
  20. 25 14
      hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/MultipartRequestInputStream.java
  21. 24 25
      hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/UploadFile.java
  22. 1 1
      hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/UploadFileHeader.java
  23. 9 9
      hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/UploadSetting.java
  24. 7 0
      hutool-core/src/main/java/cn/hutool/core/net/multipart/package-info.java
  25. 12 1
      hutool-core/src/main/java/cn/hutool/core/util/CharsetUtil.java
  26. 1 1
      hutool-cron/pom.xml
  27. 1 1
      hutool-crypto/pom.xml
  28. 1 1
      hutool-db/pom.xml
  29. 1 1
      hutool-dfa/pom.xml
  30. 1 1
      hutool-extra/pom.xml
  31. 3 3
      hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java
  32. 0 7
      hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/package-info.java
  33. 1 1
      hutool-http/pom.xml
  34. 22 4
      hutool-http/src/main/java/cn/hutool/http/ContentType.java
  35. 39 11
      hutool-http/src/main/java/cn/hutool/http/HttpUtil.java
  36. 5 0
      hutool-http/src/main/java/cn/hutool/http/server/HttpServerBase.java
  37. 93 23
      hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java
  38. 118 8
      hutool-http/src/main/java/cn/hutool/http/server/HttpServerResponse.java
  39. 5 2
      hutool-http/src/main/java/cn/hutool/http/server/SimpleServer.java
  40. 62 0
      hutool-http/src/main/java/cn/hutool/http/server/action/RootAction.java
  41. 4 1
      hutool-http/src/main/java/cn/hutool/http/server/handler/ActionHandler.java
  42. 0 91
      hutool-http/src/main/java/cn/hutool/http/server/handler/HandlerUtil.java
  43. 0 45
      hutool-http/src/main/java/cn/hutool/http/server/handler/RootHandler.java
  44. 12 2
      hutool-http/src/test/java/cn/hutool/http/server/SimpleServerTest.java
  45. 17 8
      hutool-http/src/test/java/cn/hutool/http/test/HttpUtilTest.java
  46. 1 1
      hutool-json/pom.xml
  47. 1 1
      hutool-log/pom.xml
  48. 1 1
      hutool-poi/pom.xml
  49. 1 1
      hutool-script/pom.xml
  50. 1 1
      hutool-setting/pom.xml
  51. 1 1
      hutool-socket/pom.xml
  52. 1 1
      hutool-system/pom.xml
  53. 1 1
      pom.xml

+ 3 - 0
CHANGELOG.md

@@ -18,9 +18,12 @@
 * 【core   】     CollUtil增加toMap方法
 * 【core   】     CollUtil和IterUtil废弃一些方法
 * 【core   】     添加ValidateObjectInputStream避免对象反序列化漏洞风险
+* 【core   】     添加BiMap
+* 【all    】     cn.hutool.extra.servlet.multipart包迁移到cn.hutool.core.net下
 
 ### Bug修复
 * 【extra  】     修复SpringUtil使用devtools重启报错问题
+* 【http   】     修复HttpUtil.encodeParams针对无参数URL问题(issue#817@Github)
 
 -------------------------------------------------------------------------------------------------------------
 

+ 5 - 5
README.md

@@ -40,7 +40,7 @@
 	-- 主页:<a href="https://hutool.cn">https://hutool.cn/</a> | <a href="https://www.hutool.club/">https://www.hutool.club/</a> --
 </p>
 <p align="center">
-	-- QQ群③:<a href="https://shang.qq.com/wpa/qunwpa?idkey=35764b2247c46ffebe28e45.2.6b2af8f5dee5efcf47ceec69d21e4521aa8c75">555368316</a> --
+	-- QQ群③:<a href="https://shang.qq.com/wpa/qunwpa?idkey=35764b2247c46ffebe28e45.3.0b2af8f5dee5efcf47ceec69d21e4521aa8c75">555368316</a> --
 	-- QQ群④:<a href="https://shang.qq.com/wpa/qunwpa?idkey=309056e409a304a454c7ba250a10d38dd82b9b49cd0e1f180fedbde78b02ae0d">718802356</a> --
 </p>
 
@@ -116,21 +116,21 @@ Hutool的存在就是为了减少代码搜索成本,避免网络上参差不
 <dependency>
     <groupId>cn.hutool</groupId>
     <artifactId>hutool-all</artifactId>
-    <version>5.2.6</version>
+    <version>5.3.0</version>
 </dependency>
 ```
 
 ### Gradle
 ```
-compile 'cn.hutool:hutool-all:5.2.6'
+compile 'cn.hutool:hutool-all:5.3.0'
 ```
 
 ### 非Maven项目
 
 点击以下任一链接,下载`hutool-all-X.X.X.jar`即可:
 
-- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.2.6/)
-- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.2.6/)
+- [Maven中央库1](https://repo1.maven.org/maven2/cn/hutool/hutool-all/5.3.0/)
+- [Maven中央库2](http://repo2.maven.org/maven2/cn/hutool/hutool-all/5.3.0/)
 
 > 注意
 > Hutool 5.x支持JDK8+,对Android平台没有测试,不能保证所有工具类获工具方法可用。

+ 1 - 1
bin/version.txt

@@ -1 +1 @@
-5.2.6
+5.3.0

+ 1 - 1
docs/js/version.js

@@ -1 +1 @@
-var version = '5.2.6'
+var version = '5.3.0'

+ 1 - 1
hutool-all/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-all</artifactId>

+ 1 - 1
hutool-aop/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-aop</artifactId>

+ 1 - 1
hutool-bloomFilter/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-bloomFilter</artifactId>

+ 1 - 1
hutool-bom/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-bom</artifactId>

+ 1 - 1
hutool-cache/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-cache</artifactId>

+ 1 - 1
hutool-captcha/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-captcha</artifactId>

+ 1 - 1
hutool-core/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-core</artifactId>

+ 7 - 7
hutool-core/src/main/java/cn/hutool/core/codec/Base64.java

@@ -1,14 +1,14 @@
 package cn.hutool.core.codec;
 
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.CharsetUtil;
+
 import java.io.File;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.charset.Charset;
 
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.CharsetUtil;
-
 /**
  * Base64工具类,提供Base64的编码和解码方案<br>
  * base64编码是用64(2的6次方)个ASCII字符来表示256(2的8次方)个ASCII字符,<br>
@@ -72,7 +72,7 @@ public class Base64 {
 	 * @return 被加密后的字符串
 	 */
 	public static String encode(CharSequence source, String charset) {
-		return Base64Encoder.encode(source, CharsetUtil.charset(charset));
+		return encode(source, CharsetUtil.charset(charset));
 	}
 
 	/**
@@ -84,7 +84,7 @@ public class Base64 {
 	 * @since 3.0.6
 	 */
 	public static String encodeUrlSafe(CharSequence source, String charset) {
-		return Base64Encoder.encodeUrlSafe(source, CharsetUtil.charset(charset));
+		return encodeUrlSafe(source, CharsetUtil.charset(charset));
 	}
 
 	/**
@@ -272,7 +272,7 @@ public class Base64 {
 	 * @return 被加密后的字符串
 	 */
 	public static String decodeStr(CharSequence source, String charset) {
-		return Base64Decoder.decodeStr(source, CharsetUtil.charset(charset));
+		return decodeStr(source, CharsetUtil.charset(charset));
 	}
 
 	/**

+ 71 - 0
hutool-core/src/main/java/cn/hutool/core/map/BiMap.java

@@ -0,0 +1,71 @@
+package cn.hutool.core.map;
+
+import java.util.Map;
+
+/**
+ * 双向Map<br>
+ * 互换键值对不检查值是否有重复,如果有则后加入的元素替换先加入的元素<br>
+ * 值的顺序在HashMap中不确定,所以谁覆盖谁也不确定,在有序的Map中按照先后顺序覆盖,保留最后的值<br>
+ * 它与TableMap的区别是,BiMap维护两个Map实现高效的正向和反向查找
+ *
+ * @param <K> 键类型
+ * @param <V> 值类型
+ * @since 5.2.6
+ */
+public class BiMap<K, V> extends MapWrapper<K, V> {
+
+	private Map<V, K> inverse;
+
+	/**
+	 * 构造
+	 *
+	 * @param raw 被包装的Map
+	 */
+	public BiMap(Map<K, V> raw) {
+		super(raw);
+	}
+
+	@Override
+	public V put(K key, V value) {
+		if (null != this.inverse) {
+			this.inverse.put(value, key);
+		}
+		return super.put(key, value);
+	}
+
+	@Override
+	public void putAll(Map<? extends K, ? extends V> m) {
+		super.putAll(m);
+		if (null != this.inverse) {
+			m.forEach((key, value) -> this.inverse.put(value, key));
+		}
+	}
+
+	@Override
+	public void clear() {
+		super.clear();
+		this.inverse = null;
+	}
+
+	/**
+	 * 获取反向Map
+	 *
+	 * @return 反向Map
+	 */
+	public Map<V, K> getInverse() {
+		if (null == this.inverse) {
+			inverse = MapUtil.inverse(getRaw());
+		}
+		return this.inverse;
+	}
+
+	/**
+	 * 根据值获得键
+	 *
+	 * @param value 值
+	 * @return 键
+	 */
+	public K getKey(V value) {
+		return getInverse().get(value);
+	}
+}

+ 20 - 0
hutool-core/src/main/java/cn/hutool/core/map/MapUtil.java

@@ -681,11 +681,14 @@ public class MapUtil {
 
 	/**
 	 * Map的键和值互换
+	 * 互换键值对不检查值是否有重复,如果有则后加入的元素替换先加入的元素<br>
+	 * 值的顺序在HashMap中不确定,所以谁覆盖谁也不确定,在有序的Map中按照先后顺序覆盖,保留最后的值
 	 *
 	 * @param <T> 键和值类型
 	 * @param map Map对象,键值类型必须一致
 	 * @return 互换后的Map
 	 * @since 3.2.2
+	 * @see #inverse(Map)
 	 */
 	public static <T> Map<T, T> reverse(Map<T, T> map) {
 		return filter(map, (Editor<Entry<T, T>>) t -> new Entry<T, T>() {
@@ -708,6 +711,23 @@ public class MapUtil {
 	}
 
 	/**
+	 * Map的键和值互换<br>
+	 * 互换键值对不检查值是否有重复,如果有则后加入的元素替换先加入的元素<br>
+	 * 值的顺序在HashMap中不确定,所以谁覆盖谁也不确定,在有序的Map中按照先后顺序覆盖,保留最后的值
+	 *
+	 * @param <K> 键和值类型
+	 * @param <V> 键和值类型
+	 * @param map Map对象,键值类型必须一致
+	 * @return 互换后的Map
+	 * @since 5.2.6
+	 */
+	public static <K, V> Map<V, K> inverse(Map<K, V> map) {
+		final Map<V, K> result = createMap(map.getClass());
+		map.forEach((key, value) -> result.put(value, key));
+		return result;
+	}
+
+	/**
 	 * 排序已有Map,Key有序的Map,使用默认Key排序方式(字母顺序)
 	 *
 	 * @param <K> key的类型

+ 13 - 11
hutool-core/src/main/java/cn/hutool/core/map/TableMap.java

@@ -16,12 +16,13 @@ import java.util.Objects;
 import java.util.Set;
 
 /**
- * 可重复键的Map
- * 
- * @author looly
+ * 可重复键和值的Map<br>
+ * 通过键值单独建立List方式,使键值对一一对应,实现正向和反向两种查找<br>
+ * 无论是正向还是反向,都是遍历列表查找过程,相比标准的HashMap要慢,数据越多越慢
  *
  * @param <K> 键类型
  * @param <V> 值类型
+ * @author looly
  */
 public class TableMap<K, V> implements Map<K, V>, Serializable {
 	private static final long serialVersionUID = 1L;
@@ -31,7 +32,7 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
 
 	/**
 	 * 构造
-	 * 
+	 *
 	 * @param size 初始容量
 	 */
 	public TableMap(int size) {
@@ -41,8 +42,8 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
 
 	/**
 	 * 构造
-	 * 
-	 * @param keys 键列表
+	 *
+	 * @param keys   键列表
 	 * @param values 值列表
 	 */
 	public TableMap(K[] keys, V[] values) {
@@ -89,10 +90,10 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
 	 * @return 值列表
 	 * @since 5.2.5
 	 */
-	public List<V> getValues(K key){
+	public List<V> getValues(K key) {
 		return CollUtil.getAny(
 				this.values,
-				ListUtil.indexOfAll(this.keys, (ele)-> ObjectUtil.equal(ele, key))
+				ListUtil.indexOfAll(this.keys, (ele) -> ObjectUtil.equal(ele, key))
 		);
 	}
 
@@ -103,10 +104,10 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
 	 * @return 值列表
 	 * @since 5.2.5
 	 */
-	public List<K> getKeys(V value){
+	public List<K> getKeys(V value) {
 		return CollUtil.getAny(
 				this.keys,
-				ListUtil.indexOfAll(this.values, (ele)-> ObjectUtil.equal(ele, value))
+				ListUtil.indexOfAll(this.values, (ele) -> ObjectUtil.equal(ele, value))
 		);
 	}
 
@@ -189,12 +190,13 @@ public class TableMap<K, V> implements Map<K, V>, Serializable {
 		public V setValue(V value) {
 			throw new UnsupportedOperationException("setValue not supported.");
 		}
+
 		@Override
 		public final boolean equals(Object o) {
 			if (o == this)
 				return true;
 			if (o instanceof Map.Entry) {
-				Map.Entry<?,?> e = (Map.Entry<?,?>)o;
+				Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
 				return Objects.equals(key, e.getKey()) &&
 						Objects.equals(value, e.getValue());
 			}

+ 6 - 6
hutool-core/src/main/java/cn/hutool/core/map/multi/CollectionValueMap.java

@@ -1,12 +1,12 @@
 package cn.hutool.core.map.multi;
 
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.map.MapWrapper;
+
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
-import cn.hutool.core.collection.CollUtil;
-import cn.hutool.core.map.MapWrapper;
-
 /**
  * 值作为集合的Map实现,通过调用putValue可以在相同key时加入多个值,多个值用集合表示
  * 
@@ -67,7 +67,7 @@ public abstract class CollectionValueMap<K, V> extends MapWrapper<K, Collection<
 	 * @param loadFactor 加载因子
 	 */
 	public CollectionValueMap(int initialCapacity, float loadFactor) {
-		super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor));
+		super(new HashMap<>(initialCapacity, loadFactor));
 	}
 	// ------------------------------------------------------------------------- Constructor end
 
@@ -81,7 +81,7 @@ public abstract class CollectionValueMap<K, V> extends MapWrapper<K, Collection<
 	public void putValue(K key, V value) {
 		Collection<V> collection = this.get(key);
 		if (null == collection) {
-			collection = createCollction();
+			collection = createCollection();
 			this.put(key, collection);
 		}
 		collection.add(value);
@@ -105,5 +105,5 @@ public abstract class CollectionValueMap<K, V> extends MapWrapper<K, Collection<
 	 * 
 	 * @return {@link Collection}
 	 */
-	protected abstract Collection<V> createCollction();
+	protected abstract Collection<V> createCollection();
 }

+ 2 - 2
hutool-core/src/main/java/cn/hutool/core/map/multi/ListValueMap.java

@@ -62,7 +62,7 @@ public class ListValueMap<K, V> extends CollectionValueMap<K, V> {
 	 * @param loadFactor 加载因子
 	 */
 	public ListValueMap(int initialCapacity, float loadFactor) {
-		super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor));
+		super(new HashMap<>(initialCapacity, loadFactor));
 	}
 	// ------------------------------------------------------------------------- Constructor end
 
@@ -72,7 +72,7 @@ public class ListValueMap<K, V> extends CollectionValueMap<K, V> {
 	}
 	
 	@Override
-	protected Collection<V> createCollction() {
+	protected Collection<V> createCollection() {
 		return new ArrayList<>(DEFAULT_COLLCTION_INITIAL_CAPACITY);
 	}
 }

+ 2 - 2
hutool-core/src/main/java/cn/hutool/core/map/multi/SetValueMap.java

@@ -62,7 +62,7 @@ public class SetValueMap<K, V> extends CollectionValueMap<K, V> {
 	 * @param loadFactor 加载因子
 	 */
 	public SetValueMap(int initialCapacity, float loadFactor) {
-		super(new HashMap<K, Collection<V>>(initialCapacity, loadFactor));
+		super(new HashMap<>(initialCapacity, loadFactor));
 	}
 	// ------------------------------------------------------------------------- Constructor end
 
@@ -72,7 +72,7 @@ public class SetValueMap<K, V> extends CollectionValueMap<K, V> {
 	}
 	
 	@Override
-	protected Collection<V> createCollction() {
+	protected Collection<V> createCollection() {
 		return new LinkedHashSet<>(DEFAULT_COLLCTION_INITIAL_CAPACITY);
 	}
 }

+ 7 - 18
hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/MultipartFormData.java

@@ -1,17 +1,16 @@
-package cn.hutool.extra.servlet.multipart;
+package cn.hutool.core.net.multipart;
+
+import cn.hutool.core.util.ArrayUtil;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.charset.Charset;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
 
-import javax.servlet.ServletRequest;
-
-import cn.hutool.core.util.ArrayUtil;
-
 /**
  * HttpRequest解析器<br>
  * 来自Jodd
@@ -21,9 +20,9 @@ import cn.hutool.core.util.ArrayUtil;
 public class MultipartFormData {
 
 	/** 请求参数 */
-	private Map<String, String[]> requestParameters = new HashMap<String, String[]>();
+	private Map<String, String[]> requestParameters = new HashMap<>();
 	/** 请求文件 */
-	private Map<String, UploadFile[]> requestFiles = new HashMap<String, UploadFile[]>();
+	private Map<String, UploadFile[]> requestFiles = new HashMap<>();
 
 	/** 是否解析完毕 */
 	private boolean loaded;
@@ -49,23 +48,13 @@ public class MultipartFormData {
 	// --------------------------------------------------------------------- Constructor end
 
 	/**
-	 * 解析上传文件和表单数据
-	 * 
-	 * @param request Http请求
-	 * @throws IOException IO异常
-	 */
-	public void parseRequest(ServletRequest request) throws IOException {
-		parseRequestStream(request.getInputStream(), request.getCharacterEncoding());
-	}
-
-	/**
 	 * 提取上传的文件和表单数据
 	 * 
 	 * @param inputStream HttpRequest流
 	 * @param charset 编码
 	 * @throws IOException IO异常
 	 */
-	public void parseRequestStream(InputStream inputStream, String charset) throws IOException {
+	public void parseRequestStream(InputStream inputStream, Charset charset) throws IOException {
 		setLoaded();
 
 		MultipartRequestInputStream input = new MultipartRequestInputStream(inputStream);

+ 25 - 14
hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/MultipartRequestInputStream.java

@@ -1,15 +1,16 @@
-package cn.hutool.extra.servlet.multipart;
+package cn.hutool.core.net.multipart;
 
 import java.io.BufferedInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.charset.Charset;
 
 /**
  * Http请求解析流,提供了专门针对带文件的form表单的解析<br>
  * 来自Jodd
- * 
+ *
  * @author jodd.org
  */
 public class MultipartRequestInputStream extends BufferedInputStream {
@@ -20,7 +21,7 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 
 	/**
 	 * 读取byte字节流,在末尾抛出异常
-	 * 
+	 *
 	 * @return byte
 	 * @throws IOException 读取异常
 	 */
@@ -34,7 +35,7 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 
 	/**
 	 * 跳过指定位数的 bytes.
-	 * 
+	 *
 	 * @param i 跳过的byte数
 	 * @throws IOException IO异常
 	 */
@@ -47,12 +48,14 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 
 	// ---------------------------------------------------------------- boundary
 
-	/** part部分边界 */
+	/**
+	 * part部分边界
+	 */
 	protected byte[] boundary;
 
 	/**
 	 * 输入流中读取边界
-	 * 
+	 *
 	 * @return 边界
 	 * @throws IOException 读取异常
 	 */
@@ -60,6 +63,7 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 		ByteArrayOutputStream boundaryOutput = new ByteArrayOutputStream(1024);
 		byte b;
 		// skip optional whitespaces
+		//noinspection StatementWithEmptyBody
 		while ((b = readByte()) <= ' ') {
 		}
 		boundaryOutput.write(b);
@@ -89,12 +93,12 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 
 	/**
 	 * 从流中读取文件头部信息, 如果达到末尾则返回null
-	 * 
+	 *
 	 * @param encoding 字符集
 	 * @return 头部信息, 如果达到末尾则返回null
 	 * @throws IOException 读取异常
 	 */
-	public UploadFileHeader readDataHeader(String encoding) throws IOException {
+	public UploadFileHeader readDataHeader(Charset encoding) throws IOException {
 		String dataHeader = readDataHeaderString(encoding);
 		if (dataHeader != null) {
 			lastHeader = new UploadFileHeader(dataHeader);
@@ -104,7 +108,14 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 		return lastHeader;
 	}
 
-	protected String readDataHeaderString(String encoding) throws IOException {
+	/**
+	 * 读取数据头信息字符串
+	 *
+	 * @param charset 编码
+	 * @return 数据头信息字符串
+	 * @throws IOException IO异常
+	 */
+	protected String readDataHeaderString(Charset charset) throws IOException {
 		ByteArrayOutputStream data = new ByteArrayOutputStream();
 		byte b;
 		while (true) {
@@ -128,13 +139,13 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 			data.write(b);
 		}
 		skipBytes(3);
-		return encoding == null ? data.toString() : data.toString(encoding);
+		return charset == null ? data.toString() : data.toString(charset.name());
 	}
 	// ---------------------------------------------------------------- copy
 
 	/**
 	 * 全部字节流复制到out
-	 * 
+	 *
 	 * @param out 输出流
 	 * @return 复制的字节数
 	 * @throws IOException 读取异常
@@ -154,8 +165,8 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 
 	/**
 	 * 复制字节流到out, 大于maxBytes或者文件末尾停止
-	 * 
-	 * @param out 输出流
+	 *
+	 * @param out   输出流
 	 * @param limit 最大字节数
 	 * @return 复制的字节数
 	 * @throws IOException 读取异常
@@ -178,7 +189,7 @@ public class MultipartRequestInputStream extends BufferedInputStream {
 
 	/**
 	 * 跳过边界表示
-	 * 
+	 *
 	 * @return 跳过的字节数
 	 * @throws IOException 读取异常
 	 */

+ 24 - 25
hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/UploadFile.java

@@ -1,4 +1,8 @@
-package cn.hutool.extra.servlet.multipart;
+package cn.hutool.core.net.multipart;
+
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
+import cn.hutool.core.util.StrUtil;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -10,27 +14,18 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.log.Log;
-import cn.hutool.log.LogFactory;
-
 /**
  * 上传的文件对象
- * 
+ *
  * @author xiaoleilu
- * 
  */
 public class UploadFile {
-	private static Log log = LogFactory.get();
-
 	private static final String TMP_FILE_PREFIX = "hutool-";
 	private static final String TMP_FILE_SUFFIX = ".upload.tmp";
 
 	private UploadFileHeader header;
 	private UploadSetting setting;
-	
+
 	private int size = -1;
 
 	// 文件流(小文件位于内存中)
@@ -40,8 +35,8 @@ public class UploadFile {
 
 	/**
 	 * 构造
-	 * 
-	 * @param header 头部信息
+	 *
+	 * @param header  头部信息
 	 * @param setting 上传设置
 	 */
 	public UploadFile(UploadFileHeader header, UploadSetting setting) {
@@ -56,6 +51,7 @@ public class UploadFile {
 	 */
 	public void delete() {
 		if (tempFile != null) {
+			//noinspection ResultOfMethodCallIgnored
 			tempFile.delete();
 		}
 		if (data != null) {
@@ -66,17 +62,18 @@ public class UploadFile {
 	/**
 	 * 将上传的文件写入指定的目标文件路径,自动创建文件<br>
 	 * 写入后原临时文件会被删除
+	 *
 	 * @param destPath 目标文件路径
 	 * @return 目标文件
 	 * @throws IOException IO异常
 	 */
 	public File write(String destPath) throws IOException {
-		if(data != null || tempFile != null) {
+		if (data != null || tempFile != null) {
 			return write(FileUtil.touch(destPath));
 		}
 		return null;
 	}
-	
+
 	/**
 	 * 将上传的文件写入目标文件<br>
 	 * 写入后原临时文件会被删除
@@ -87,7 +84,7 @@ public class UploadFile {
 	 */
 	public File write(File destination) throws IOException {
 		assertValid();
-		
+
 		if (destination.isDirectory() == true) {
 			destination = new File(destination, this.header.getFileName());
 		}
@@ -101,14 +98,14 @@ public class UploadFile {
 		}
 		return destination;
 	}
-	
+
 	/**
 	 * @return 获得文件字节流
 	 * @throws IOException IO异常
 	 */
 	public byte[] getFileContent() throws IOException {
 		assertValid();
-		
+
 		if (data != null) {
 			return data;
 		}
@@ -124,7 +121,7 @@ public class UploadFile {
 	 */
 	public InputStream getFileInputStream() throws IOException {
 		assertValid();
-		
+
 		if (data != null) {
 			return new BufferedInputStream(new ByteArrayInputStream(data));
 		}
@@ -174,9 +171,10 @@ public class UploadFile {
 	}
 
 	// ---------------------------------------------------------------- process
+
 	/**
 	 * 处理上传表单流,提取出文件
-	 * 
+	 *
 	 * @param input 上传表单的流
 	 * @return 是否成功
 	 * @throws IOException IO异常
@@ -184,7 +182,6 @@ public class UploadFile {
 	protected boolean processStream(MultipartRequestInputStream input) throws IOException {
 		if (!isAllowedExtension()) {
 			// 非允许的扩展名
-			log.debug("Forbidden uploaded file [{}]", this.getFileName());
 			size = input.skipToBoundary();
 			return false;
 		}
@@ -220,9 +217,9 @@ public class UploadFile {
 			size += input.copy(out, maxFileSize - size + 1); // one more byte to detect larger files
 			if (size > maxFileSize) {
 				// 超出上传大小限制
+				//noinspection ResultOfMethodCallIgnored
 				tempFile.delete();
 				tempFile = null;
-				log.debug("Upload file [{}] too big, file size > [{}]", this.getFileName(), maxFileSize);
 				input.skipToBoundary();
 				return false;
 			}
@@ -236,6 +233,7 @@ public class UploadFile {
 	}
 
 	// ---------------------------------------------------------------------------- Private method start
+
 	/**
 	 * @return 是否为允许的扩展名
 	 */
@@ -257,13 +255,14 @@ public class UploadFile {
 		// 未匹配到扩展名,如果为允许列表,返回false, 否则true
 		return !isAllow;
 	}
-	
+
 	/**
 	 * 断言是否文件流可用
+	 *
 	 * @throws IOException IO异常
 	 */
 	private void assertValid() throws IOException {
-		if(! isUploaded()) {
+		if (!isUploaded()) {
 			throw new IOException(StrUtil.format("File [{}] upload fail", getFileName()));
 		}
 	}

+ 1 - 1
hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/UploadFileHeader.java

@@ -1,4 +1,4 @@
-package cn.hutool.extra.servlet.multipart;
+package cn.hutool.core.net.multipart;
 
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.util.StrUtil;

+ 9 - 9
hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/UploadSetting.java

@@ -1,4 +1,4 @@
-package cn.hutool.extra.servlet.multipart;
+package cn.hutool.core.net.multipart;
 
 import cn.hutool.core.util.URLUtil;
 import cn.hutool.log.Log;
@@ -8,7 +8,7 @@ import java.net.URL;
 
 /**
  * 上传文件设定文件
- * 
+ *
  * @author xiaoleilu
  *
  */
@@ -42,7 +42,7 @@ public class UploadSetting {
 
 	/**
 	 * 设定最大文件大小,-1表示无限制
-	 * 
+	 *
 	 * @param maxFileSize 最大文件大小
 	 */
 	public void setMaxFileSize(int maxFileSize) {
@@ -59,7 +59,7 @@ public class UploadSetting {
 	/**
 	 * 设定文件保存到内存的边界<br>
 	 * 如果文件大小小于这个边界,将保存于内存中,否则保存至临时目录中
-	 * 
+	 *
 	 * @param memoryThreshold 文件保存到内存的边界
 	 */
 	public void setMemoryThreshold(int memoryThreshold) {
@@ -75,7 +75,7 @@ public class UploadSetting {
 
 	/**
 	 * 设定上传文件的临时目录,null表示使用系统临时目录
-	 * 
+	 *
 	 * @param tmpUploadPath 临时目录,绝对路径
 	 */
 	public void setTmpUploadPath(String tmpUploadPath) {
@@ -92,7 +92,7 @@ public class UploadSetting {
 	/**
 	 * 设定文件扩展名限定里列表<br>
 	 * 禁止列表还是允许列表取决于isAllowFileExts
-	 * 
+	 *
 	 * @param fileExts 文件扩展名列表
 	 */
 	public void setFileExts(String[] fileExts) {
@@ -101,7 +101,7 @@ public class UploadSetting {
 
 	/**
 	 * 是否允许文件扩展名<br>
-	 * 
+	 *
 	 * @return 若true表示只允许列表里的扩展名,否则是禁止列表里的扩展名
 	 */
 	public boolean isAllowFileExts() {
@@ -110,7 +110,7 @@ public class UploadSetting {
 
 	/**
 	 * 设定是否允许扩展名
-	 * 
+	 *
 	 * @param isAllowFileExts 若true表示只允许列表里的扩展名,否则是禁止列表里的扩展名
 	 */
 	public void setAllowFileExts(boolean isAllowFileExts) {
@@ -128,7 +128,7 @@ public class UploadSetting {
 
 	/**
 	 * 加载全局设定
-	 * 
+	 *
 	 * @param settingPath 设定文件路径,相对Classpath
 	 */
 	synchronized public void load(String settingPath) {

+ 7 - 0
hutool-core/src/main/java/cn/hutool/core/net/multipart/package-info.java

@@ -0,0 +1,7 @@
+/**
+ * 文件上传封装
+ * 
+ * @author looly
+ *
+ */
+package cn.hutool.core.net.multipart;

+ 12 - 1
hutool-core/src/main/java/cn/hutool/core/util/CharsetUtil.java

@@ -63,6 +63,17 @@ public class CharsetUtil {
 	}
 
 	/**
+	 * 解析字符串编码为Charset对象,解析失败返回系统默认编码
+	 *
+	 * @param charsetName    字符集,为空则返回默认字符集
+	 * @return Charset
+	 * @since 5.2.6
+	 */
+	public static Charset parse(String charsetName) {
+		return parse(charsetName, Charset.defaultCharset());
+	}
+
+	/**
 	 * 解析字符串编码为Charset对象,解析失败返回默认编码
 	 *
 	 * @param charsetName    字符集,为空则返回默认字符集
@@ -70,7 +81,7 @@ public class CharsetUtil {
 	 * @return Charset
 	 * @since 5.2.6
 	 */
-	public static Charset parse(String charsetName, Charset defaultCharset) throws UnsupportedCharsetException {
+	public static Charset parse(String charsetName, Charset defaultCharset) {
 		if (StrUtil.isBlank(charsetName)) {
 			return defaultCharset;
 		}

+ 1 - 1
hutool-cron/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-cron</artifactId>

+ 1 - 1
hutool-crypto/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-crypto</artifactId>

+ 1 - 1
hutool-db/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-db</artifactId>

+ 1 - 1
hutool-dfa/pom.xml

@@ -7,7 +7,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-dfa</artifactId>

+ 1 - 1
hutool-extra/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-extra</artifactId>

+ 3 - 3
hutool-extra/src/main/java/cn/hutool/extra/servlet/ServletUtil.java

@@ -11,14 +11,14 @@ import cn.hutool.core.io.IORuntimeException;
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.map.CaseInsensitiveMap;
 import cn.hutool.core.net.NetUtil;
+import cn.hutool.core.net.multipart.MultipartFormData;
+import cn.hutool.core.net.multipart.UploadSetting;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.ReflectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.URLUtil;
-import cn.hutool.extra.servlet.multipart.MultipartFormData;
-import cn.hutool.extra.servlet.multipart.UploadSetting;
 
 import javax.servlet.ServletOutputStream;
 import javax.servlet.ServletRequest;
@@ -265,7 +265,7 @@ public class ServletUtil {
 	public static MultipartFormData getMultipart(ServletRequest request, UploadSetting uploadSetting) throws IORuntimeException {
 		final MultipartFormData formData = new MultipartFormData(uploadSetting);
 		try {
-			formData.parseRequest(request);
+			formData.parseRequestStream(request.getInputStream(), request.getCharacterEncoding());
 		} catch (IOException e) {
 			throw new IORuntimeException(e);
 		}

+ 0 - 7
hutool-extra/src/main/java/cn/hutool/extra/servlet/multipart/package-info.java

@@ -1,7 +0,0 @@
-/**
- * 基于Servlet的文件上传封装
- * 
- * @author looly
- *
- */
-package cn.hutool.extra.servlet.multipart;

+ 1 - 1
hutool-http/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-http</artifactId>

+ 22 - 4
hutool-http/src/main/java/cn/hutool/http/ContentType.java

@@ -29,9 +29,17 @@ public enum ContentType {
 	 */
 	XML("application/xml"),
 	/**
+	 * text/plain编码
+	 */
+	TEXT_PLAIN("text/plain"),
+	/**
 	 * Rest请求text/xml编码
 	 */
-	TEXT_XML("text/xml");
+	TEXT_XML("text/xml"),
+	/**
+	 * text/html编码
+	 */
+	TEXT_HTML("text/html");
 
 	private String value;
 
@@ -39,9 +47,19 @@ public enum ContentType {
 		this.value = value;
 	}
 
+	/**
+	 * 获取value值
+	 *
+	 * @return value值
+	 * @since 5.2.6
+	 */
+	public String getValue() {
+		return value;
+	}
+
 	@Override
 	public String toString() {
-		return value;
+		return getValue();
 	}
 
 	/**
@@ -62,7 +80,7 @@ public enum ContentType {
 	 * @since 4.1.5
 	 */
 	public static boolean isDefault(String contentType) {
-		return null == contentType || isFormUrlEncoed(contentType);
+		return null == contentType || isFormUrlEncode(contentType);
 	}
 
 	/**
@@ -71,7 +89,7 @@ public enum ContentType {
 	 * @param contentType 内容类型
 	 * @return 是否为application/x-www-form-urlencoded
 	 */
-	public static boolean isFormUrlEncoed(String contentType) {
+	public static boolean isFormUrlEncode(String contentType) {
 		return StrUtil.startWithIgnoreCase(contentType, FORM_URLENCODED.toString());
 	}
 

+ 39 - 11
hutool-http/src/main/java/cn/hutool/http/HttpUtil.java

@@ -2,6 +2,7 @@ package cn.hutool.http;
 
 import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.IterUtil;
 import cn.hutool.core.convert.Convert;
 import cn.hutool.core.io.FastByteArrayOutputStream;
 import cn.hutool.core.io.FileUtil;
@@ -416,7 +417,7 @@ public class HttpUtil {
 			if (value instanceof Iterable) {
 				value = CollUtil.join((Iterable<?>) value, ",");
 			} else if (value instanceof Iterator) {
-				value = CollUtil.join((Iterator<?>) value, ",");
+				value = IterUtil.join((Iterator<?>) value, ",");
 			}
 			valueStr = Convert.toStr(value);
 			if (StrUtil.isNotEmpty(key)) {
@@ -435,30 +436,33 @@ public class HttpUtil {
 	 *
 	 * <p>注意,此方法只能标准化整个URL,并不适合于单独编码参数值</p>
 	 *
-	 * @param paramsStr url参数,可以包含url本身
+	 * @param urlWithParams url和参数,可以包含url本身,也可以单独参数
 	 * @param charset   编码
 	 * @return 编码后的url和参数
 	 * @since 4.0.1
 	 */
-	public static String encodeParams(String paramsStr, Charset charset) {
-		if (StrUtil.isBlank(paramsStr)) {
+	public static String encodeParams(String urlWithParams, Charset charset) {
+		if (StrUtil.isBlank(urlWithParams)) {
 			return StrUtil.EMPTY;
 		}
 
 		String urlPart = null; // url部分,不包括问号
 		String paramPart; // 参数部分
-		int pathEndPos = paramsStr.indexOf('?');
+		final int pathEndPos = urlWithParams.indexOf('?');
 		if (pathEndPos > -1) {
 			// url + 参数
-			urlPart = StrUtil.subPre(paramsStr, pathEndPos);
-			paramPart = StrUtil.subSuf(paramsStr, pathEndPos + 1);
+			urlPart = StrUtil.subPre(urlWithParams, pathEndPos);
+			paramPart = StrUtil.subSuf(urlWithParams, pathEndPos + 1);
 			if (StrUtil.isBlank(paramPart)) {
 				// 无参数,返回url
 				return urlPart;
 			}
-		} else {
-			// 无URL
-			paramPart = paramsStr;
+		} else if(false == StrUtil.contains(urlWithParams, '=')){
+			// 无参数的URL
+			return urlWithParams;
+		}else {
+			// 无URL的参数
+			paramPart = urlWithParams;
 		}
 
 		paramPart = normalizeParams(paramPart, charset);
@@ -534,6 +538,18 @@ public class HttpUtil {
 	 * @since 4.0.2
 	 */
 	public static HashMap<String, String> decodeParamMap(String paramsStr, String charset) {
+		return decodeParamMap(paramsStr, CharsetUtil.charset(charset));
+	}
+
+	/**
+	 * 将URL参数解析为Map(也可以解析Post中的键值对参数)
+	 *
+	 * @param paramsStr 参数字符串(或者带参数的Path)
+	 * @param charset   字符集
+	 * @return 参数Map
+	 * @since 5.2.6
+	 */
+	public static HashMap<String, String> decodeParamMap(String paramsStr, Charset charset) {
 		final Map<String, List<String>> paramsMap = decodeParams(paramsStr, charset);
 		final HashMap<String, String> result = MapUtil.newHashMap(paramsMap.size());
 		List<String> valueList;
@@ -552,6 +568,18 @@ public class HttpUtil {
 	 * @return 参数Map
 	 */
 	public static Map<String, List<String>> decodeParams(String paramsStr, String charset) {
+		return decodeParams(paramsStr, CharsetUtil.charset(charset));
+	}
+
+	/**
+	 * 将URL参数解析为Map(也可以解析Post中的键值对参数)
+	 *
+	 * @param paramsStr 参数字符串(或者带参数的Path)
+	 * @param charset   字符集
+	 * @return 参数Map
+	 * @since 5.2.6
+	 */
+	public static Map<String, List<String>> decodeParams(String paramsStr, Charset charset) {
 		if (StrUtil.isBlank(paramsStr)) {
 			return Collections.emptyMap();
 		}
@@ -811,7 +839,7 @@ public class HttpUtil {
 	 * @param value   value
 	 * @param charset 编码
 	 */
-	private static void addParam(Map<String, List<String>> params, String name, String value, String charset) {
+	private static void addParam(Map<String, List<String>> params, String name, String value, Charset charset) {
 		name = URLUtil.decode(name, charset);
 		value = URLUtil.decode(value, charset);
 		final List<String> values = params.computeIfAbsent(name, k -> new ArrayList<>(1));

+ 5 - 0
hutool-http/src/main/java/cn/hutool/http/server/HttpServerBase.java

@@ -1,7 +1,10 @@
 package cn.hutool.http.server;
 
+import cn.hutool.core.util.CharsetUtil;
 import com.sun.net.httpserver.HttpExchange;
 
+import java.nio.charset.Charset;
+
 /**
  * HttpServer公用对象,提供HttpExchange包装和公用方法
  *
@@ -10,6 +13,8 @@ import com.sun.net.httpserver.HttpExchange;
  */
 public class HttpServerBase {
 
+	final static Charset DEFAULT_CHARSET = CharsetUtil.CHARSET_UTF_8;
+
 	final HttpExchange httpExchange;
 
 	/**

+ 93 - 23
hutool-http/src/main/java/cn/hutool/http/server/HttpServerRequest.java

@@ -1,9 +1,13 @@
 package cn.hutool.http.server;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.IORuntimeException;
 import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.map.CaseInsensitiveMap;
+import cn.hutool.core.map.multi.ListValueMap;
 import cn.hutool.core.net.NetUtil;
+import cn.hutool.core.net.multipart.MultipartFormData;
+import cn.hutool.core.net.multipart.UploadSetting;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.StrUtil;
@@ -15,6 +19,7 @@ import cn.hutool.http.useragent.UserAgentUtil;
 import com.sun.net.httpserver.Headers;
 import com.sun.net.httpserver.HttpExchange;
 
+import java.io.IOException;
 import java.io.InputStream;
 import java.net.HttpCookie;
 import java.net.URI;
@@ -32,6 +37,9 @@ import java.util.Map;
 public class HttpServerRequest extends HttpServerBase {
 
 	private Map<String, HttpCookie> cookieCache;
+	private ListValueMap<String, String> paramsCache;
+	private Charset charsetCache;
+	private byte[] bodyCache;
 
 	/**
 	 * 构造
@@ -159,9 +167,13 @@ public class HttpServerRequest extends HttpServerBase {
 	 * @return 编码,默认UTF-8
 	 */
 	public Charset getCharset() {
-		final String contentType = getContentType();
-		final String charsetStr = HttpUtil.getCharset(contentType);
-		return CharsetUtil.parse(charsetStr, CharsetUtil.CHARSET_UTF_8);
+		if(null == this.charsetCache){
+			final String contentType = getContentType();
+			final String charsetStr = HttpUtil.getCharset(contentType);
+			this.charsetCache = CharsetUtil.parse(charsetStr, DEFAULT_CHARSET);
+		}
+
+		return this.charsetCache;
 	}
 
 	/**
@@ -226,12 +238,20 @@ public class HttpServerRequest extends HttpServerBase {
 	}
 
 	/**
-	 * 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
+	 * 是否为Multipart类型表单,此类型表单用于文件上传
 	 *
-	 * @return 
+	 * @return 是否为Multipart类型表单,此类型表单用于文件上传
 	 */
-	public InputStream getBodyStream() {
-		return this.httpExchange.getRequestBody();
+	public boolean isMultipart() {
+		if (false == isPostMethod()) {
+			return false;
+		}
+
+		final String contentType = getContentType();
+		if (StrUtil.isBlank(contentType)) {
+			return false;
+		}
+		return contentType.toLowerCase().startsWith("multipart/");
 	}
 
 	/**
@@ -251,31 +271,49 @@ public class HttpServerRequest extends HttpServerBase {
 	 * @return 请求
 	 */
 	public String getBody(Charset charset) {
-		InputStream in = null;
-		try {
-			in = getBodyStream();
-			return IoUtil.read(in, charset);
-		} finally {
-			IoUtil.close(in);
-		}
+		return StrUtil.str(getBodyBytes(), charset);
 	}
 
 	/**
-	 * 是否为Multipart类型表单,此类型表单用于文件上传
+	 * 获取body的bytes数组
 	 *
-	 * @return 是否为Multipart类型表单,此类型表单用于文件上传
+	 * @return body的bytes数组
 	 */
-	public boolean isMultipart() {
-		if (false == isPostMethod()) {
-			return false;
+	public byte[] getBodyBytes(){
+		if(null == this.bodyCache){
+			this.bodyCache = IoUtil.readBytes(getBodyStream(), true);
 		}
+		return this.bodyCache;
+	}
 
-		final String contentType = getContentType();
-		if (StrUtil.isBlank(contentType)) {
-			return false;
+	/**
+	 * 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
+	 *
+	 * @return 流
+	 */
+	public InputStream getBodyStream() {
+		return this.httpExchange.getRequestBody();
+	}
+
+	public ListValueMap<String, String> getParams() {
+		if (null == this.paramsCache) {
+			this.paramsCache = new ListValueMap<>();
+			final Charset charset = getCharset();
+
+			//解析URL中的参数
+			final String query = getQuery();
+			if(StrUtil.isNotBlank(query)){
+				this.paramsCache.putAll(HttpUtil.decodeParams(query, charset));
+			}
+
+			// 解析body中的参数
+			final String body = getBody();
+			if(StrUtil.isNotBlank(body)){
+				this.paramsCache.putAll(HttpUtil.decodeParams(body, charset));
+			}
 		}
 
-		return contentType.toLowerCase().startsWith("multipart/");
+		return this.paramsCache;
 	}
 
 	/**
@@ -332,4 +370,36 @@ public class HttpServerRequest extends HttpServerBase {
 		ip = this.httpExchange.getRemoteAddress().getHostName();
 		return NetUtil.getMultistageReverseProxyIp(ip);
 	}
+
+	/**
+	 * 获得MultiPart表单内容,多用于获得上传的文件 在同一次请求中,此方法只能被执行一次!
+	 *
+	 * @return MultipartFormData
+	 * @throws IORuntimeException IO异常
+	 * @since 5.3.0
+	 */
+	public MultipartFormData getMultipart() throws IORuntimeException {
+		return getMultipart(new UploadSetting());
+	}
+
+	/**
+	 * 获得multipart/form-data 表单内容<br>
+	 * 包括文件和普通表单数据<br>
+	 * 在同一次请求中,此方法只能被执行一次!
+	 *
+	 * @param uploadSetting 上传文件的设定,包括最大文件大小、保存在内存的边界大小、临时目录、扩展名限定等
+	 * @return MultiPart表单
+	 * @throws IORuntimeException IO异常
+	 * @since 5.3.0
+	 */
+	public MultipartFormData getMultipart(UploadSetting uploadSetting) throws IORuntimeException {
+		final MultipartFormData formData = new MultipartFormData(uploadSetting);
+		try {
+			formData.parseRequestStream(getBodyStream(), getCharset());
+		} catch (IOException e) {
+			throw new IORuntimeException(e);
+		}
+
+		return formData;
+	}
 }

+ 118 - 8
hutool-http/src/main/java/cn/hutool/http/server/HttpServerResponse.java

@@ -3,12 +3,14 @@ package cn.hutool.http.server;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.io.IORuntimeException;
 import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.CharsetUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.core.util.URLUtil;
+import cn.hutool.http.ContentType;
 import cn.hutool.http.Header;
+import cn.hutool.http.HttpStatus;
 import cn.hutool.http.HttpUtil;
+
 import com.sun.net.httpserver.Headers;
 import com.sun.net.httpserver.HttpExchange;
 
@@ -18,6 +20,8 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
 import java.nio.charset.Charset;
 import java.util.List;
 import java.util.Map;
@@ -28,6 +32,10 @@ import java.util.Map;
 public class HttpServerResponse extends HttpServerBase {
 
 	private Charset charset;
+	/**
+	 * 是否已经发送了Http状态码,如果没有,提前写出状态码
+	 */
+	private boolean isSendCode;
 
 	/**
 	 * 构造
@@ -49,6 +57,38 @@ public class HttpServerResponse extends HttpServerBase {
 	}
 
 	/**
+	 * 发送成功状态码
+	 *
+	 * @return this
+	 */
+	public HttpServerResponse sendOk() {
+		return send(HttpStatus.HTTP_OK);
+	}
+
+	/**
+	 * 发送404错误页
+	 *
+	 * @param content 错误页页面内容,默认text/html类型
+	 * @return this
+	 */
+	public HttpServerResponse send404(String content) {
+		return sendError(HttpStatus.HTTP_NOT_FOUND, content);
+	}
+
+	/**
+	 * 发送错误页
+	 *
+	 * @param errorCode HTTP错误状态码,见HttpStatus
+	 * @param content   错误页页面内容,默认text/html类型
+	 * @return this
+	 */
+	public HttpServerResponse sendError(int errorCode, String content) {
+		send(errorCode);
+		setContentType(ContentType.TEXT_HTML.toString());
+		return write(content);
+	}
+
+	/**
 	 * 发送HTTP状态码
 	 *
 	 * @param httpStatusCode HTTP状态码,见HttpStatus
@@ -56,11 +96,17 @@ public class HttpServerResponse extends HttpServerBase {
 	 * @return this
 	 */
 	public HttpServerResponse send(int httpStatusCode, long bodyLength) {
+		if (this.isSendCode) {
+			throw new IORuntimeException("Http status code has been send!");
+		}
+
 		try {
 			this.httpExchange.sendResponseHeaders(httpStatusCode, bodyLength);
 		} catch (IOException e) {
 			throw new IORuntimeException(e);
 		}
+
+		this.isSendCode = true;
 		return this;
 	}
 
@@ -70,6 +116,9 @@ public class HttpServerResponse extends HttpServerBase {
 	 * @return 响应头
 	 */
 	public Headers getHeaders() {
+		if (false == this.isSendCode) {
+			sendOk();
+		}
 		return this.httpExchange.getResponseHeaders();
 	}
 
@@ -141,7 +190,7 @@ public class HttpServerResponse extends HttpServerBase {
 	public HttpServerResponse setContentType(String contentType) {
 		if (null != contentType && null != this.charset) {
 			if (false == contentType.contains(";charset=")) {
-				contentType += ";charset=" + this.charset;
+				contentType = ContentType.build(contentType, this.charset);
 			}
 		}
 
@@ -170,11 +219,26 @@ public class HttpServerResponse extends HttpServerBase {
 	}
 
 	/**
+	 * 设置属性
+	 *
+	 * @param name  属性名
+	 * @param value 属性值
+	 * @return this
+	 */
+	public HttpServerResponse setAttr(String name, Object value) {
+		this.httpExchange.setAttribute(name, value);
+		return this;
+	}
+
+	/**
 	 * 获取响应数据流
 	 *
 	 * @return 响应数据流
 	 */
 	public OutputStream getOut() {
+		if (false == this.isSendCode) {
+			sendOk();
+		}
 		return this.httpExchange.getResponseBody();
 	}
 
@@ -183,8 +247,43 @@ public class HttpServerResponse extends HttpServerBase {
 	 *
 	 * @return 响应数据流
 	 */
-	public OutputStream getWriter() {
-		return this.httpExchange.getResponseBody();
+	public PrintWriter getWriter() {
+		final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
+		return new PrintWriter(new OutputStreamWriter(getOut(), charset));
+	}
+
+	/**
+	 * 写出数据到客户端
+	 *
+	 * @param data 数据
+	 * @return this
+	 */
+	public HttpServerResponse write(String data, String contentType) {
+		setContentType(contentType);
+		return write(data);
+	}
+
+	/**
+	 * 写出数据到客户端
+	 *
+	 * @param data 数据
+	 * @return this
+	 */
+	public HttpServerResponse write(String data) {
+		final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
+		return write(StrUtil.bytes(data, charset));
+	}
+
+	/**
+	 * 写出数据到客户端
+	 *
+	 * @param data        数据
+	 * @param contentType 返回的类型
+	 * @return this
+	 */
+	public HttpServerResponse write(byte[] data, String contentType) {
+		setContentType(contentType);
+		return write(data);
 	}
 
 	/**
@@ -194,8 +293,19 @@ public class HttpServerResponse extends HttpServerBase {
 	 * @return this
 	 */
 	public HttpServerResponse write(byte[] data) {
-		write(new ByteArrayInputStream(data));
-		return this;
+		return write(new ByteArrayInputStream(data));
+	}
+
+	/**
+	 * 返回数据给客户端
+	 *
+	 * @param in          需要返回客户端的内容
+	 * @param contentType 返回的类型
+	 * @since 5.2.6
+	 */
+	public HttpServerResponse write(InputStream in, String contentType) {
+		setContentType(contentType);
+		return write(in);
 	}
 
 	/**
@@ -236,7 +346,7 @@ public class HttpServerResponse extends HttpServerBase {
 	}
 
 	/**
-	 * 返回数据给客户端
+	 * 返回文件数据给客户端(文件下载)
 	 *
 	 * @param in          需要返回客户端的内容
 	 * @param contentType 返回的类型
@@ -244,7 +354,7 @@ public class HttpServerResponse extends HttpServerBase {
 	 * @since 5.2.6
 	 */
 	public void write(InputStream in, String contentType, String fileName) {
-		final Charset charset = ObjectUtil.defaultIfNull(this.charset, CharsetUtil.CHARSET_UTF_8);
+		final Charset charset = ObjectUtil.defaultIfNull(this.charset, DEFAULT_CHARSET);
 		setHeader("Content-Disposition", StrUtil.format("attachment;filename={}", URLUtil.encode(fileName, charset)));
 		setContentType(contentType);
 		write(in);

+ 5 - 2
hutool-http/src/main/java/cn/hutool/http/server/SimpleServer.java

@@ -1,9 +1,10 @@
 package cn.hutool.http.server;
 
 import cn.hutool.core.io.IORuntimeException;
+import cn.hutool.core.lang.Console;
 import cn.hutool.http.server.action.Action;
+import cn.hutool.http.server.action.RootAction;
 import cn.hutool.http.server.handler.ActionHandler;
-import cn.hutool.http.server.handler.RootHandler;
 import com.sun.net.httpserver.HttpHandler;
 import com.sun.net.httpserver.HttpServer;
 
@@ -72,7 +73,7 @@ public class SimpleServer {
 	 * @return this
 	 */
 	public SimpleServer setRoot(String root) {
-		return addHandler("/", new RootHandler(root));
+		return addAction("/", new RootAction(root));
 	}
 
 	/**
@@ -119,6 +120,8 @@ public class SimpleServer {
 	 * 启动Http服务器,启动后会阻塞当前线程
 	 */
 	public void start() {
+		final InetSocketAddress address = getAddress();
+		Console.log("Hutool Simple Http Server listen on 【{}:{}】", address.getHostName(), address.getPort());
 		this.server.start();
 	}
 }

+ 62 - 0
hutool-http/src/main/java/cn/hutool/http/server/action/RootAction.java

@@ -0,0 +1,62 @@
+package cn.hutool.http.server.action;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.io.FileUtil;
+import cn.hutool.http.server.HttpServerRequest;
+import cn.hutool.http.server.HttpServerResponse;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * 默认的处理器,通过解析用户传入的path,找到网页根目录下对应文件后返回
+ *
+ * @author looly
+ * @since 5.2.6
+ */
+public class RootAction implements Action {
+
+	public static final String DEFAULT_INDEX_FILE_NAME = "index.html";
+
+	private final String rootDir;
+	private List<String> indexFileNames;
+
+	/**
+	 * 构造
+	 *
+	 * @param rootDir 网页根目录
+	 */
+	public RootAction(String rootDir) {
+		this(rootDir, DEFAULT_INDEX_FILE_NAME);
+	}
+
+	/**
+	 * 构造
+	 *
+	 * @param rootDir        网页根目录
+	 * @param indexFileNames 主页文件名列表
+	 */
+	public RootAction(String rootDir, String... indexFileNames) {
+		this.rootDir = rootDir;
+		this.indexFileNames = CollUtil.toList(indexFileNames);
+	}
+
+	@Override
+	public void doAction(HttpServerRequest request, HttpServerResponse response) {
+		final String path = request.getPath();
+		File file = FileUtil.file(rootDir, path);
+		if (file.exists()) {
+			if (file.isDirectory()) {
+				for (String indexFileName : indexFileNames) {
+					//默认读取主页
+					file = FileUtil.file(file, indexFileName);
+					if (file.exists() && file.isFile()) {
+						response.write(file);
+					}
+				}
+			}
+		}
+
+		response.send404("404 Not Found !");
+	}
+}

+ 4 - 1
hutool-http/src/main/java/cn/hutool/http/server/handler/ActionHandler.java

@@ -27,6 +27,9 @@ public class ActionHandler implements HttpHandler {
 
 	@Override
 	public void handle(HttpExchange httpExchange) {
-		action.doAction(new HttpServerRequest(httpExchange), new HttpServerResponse(httpExchange));
+		action.doAction(
+				new HttpServerRequest(httpExchange),
+				new HttpServerResponse(httpExchange)
+		);
 	}
 }

+ 0 - 91
hutool-http/src/main/java/cn/hutool/http/server/handler/HandlerUtil.java

@@ -1,91 +0,0 @@
-package cn.hutool.http.server.handler;
-
-import cn.hutool.core.io.FileUtil;
-import cn.hutool.core.io.IoUtil;
-import cn.hutool.core.util.ArrayUtil;
-import cn.hutool.http.Header;
-import cn.hutool.http.HttpStatus;
-import cn.hutool.http.HttpUtil;
-import com.sun.net.httpserver.HttpExchange;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * 请求处理器相关工具类
- *
- * @since 5.2.6
- */
-public class HandlerUtil {
-
-	/**
-	 * 返回404页面
-	 *
-	 * @param httpExchange HttpExchange
-	 * @param content      要发送的404页面内容
-	 * @throws IOException IO异常
-	 */
-	public static void send404(HttpExchange httpExchange, String content) throws IOException {
-		if (null == httpExchange) {
-			return;
-		}
-
-		if (null == content) {
-			content = "404 Not Found !";
-		}
-
-		httpExchange.sendResponseHeaders(HttpStatus.HTTP_NOT_FOUND, 0);
-		try (OutputStream out = httpExchange.getResponseBody()) {
-			IoUtil.writeUtf8(out, false, content);
-		}
-	}
-
-	/**
-	 * 返回文件
-	 *
-	 * @param httpExchange HttpExchange
-	 * @param file         要发送的文件
-	 * @throws IOException IO异常
-	 */
-	public static void sendFile(HttpExchange httpExchange, File file) throws IOException {
-		if (ArrayUtil.hasNull(httpExchange, file)) {
-			return;
-		}
-		addHeader(httpExchange,
-				Header.CONTENT_TYPE.toString(),
-				HttpUtil.getMimeType(file.getName(), "text/html"));
-		httpExchange.sendResponseHeaders(HttpStatus.HTTP_OK, 0);
-		try (OutputStream out = httpExchange.getResponseBody()) {
-			FileUtil.writeToStream(file, out);
-		}
-	}
-
-	/**
-	 * 增加响应头信息
-	 *
-	 * @param httpExchange HttpExchange
-	 * @param header       头名
-	 * @param value        头值
-	 */
-	public static void addHeader(HttpExchange httpExchange, String header, String value) {
-		if (ArrayUtil.hasEmpty(httpExchange, header)) {
-			return;
-		}
-		httpExchange.getResponseHeaders().add(header, value);
-	}
-
-	/**
-	 * 获取响应头信息
-	 *
-	 * @param httpExchange HttpExchange
-	 * @param header       头名
-	 * @return 值,不存在返回null
-	 */
-	public static String getHeader(HttpExchange httpExchange, String header) {
-		if (ArrayUtil.hasEmpty(httpExchange, header)) {
-			return null;
-		}
-		return httpExchange.getRequestHeaders().getFirst(header);
-	}
-}

+ 0 - 45
hutool-http/src/main/java/cn/hutool/http/server/handler/RootHandler.java

@@ -1,45 +0,0 @@
-package cn.hutool.http.server.handler;
-
-import cn.hutool.core.io.FileUtil;
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URI;
-
-/**
- * 默认的处理器,通过解析用户传入的path,找到网页根目录下对应文件后返回
- *
- * @author looly
- * @since 5.2.6
- */
-public class RootHandler implements HttpHandler {
-
-	private final String rootDir;
-
-	/**
-	 * 构造
-	 *
-	 * @param rootDir 网页根目录
-	 */
-	public RootHandler(String rootDir) {
-		this.rootDir = rootDir;
-	}
-
-	@Override
-	public void handle(HttpExchange httpExchange) throws IOException {
-		final URI uri = httpExchange.getRequestURI();
-		File file = FileUtil.file(rootDir, uri.getPath());
-		if (file.exists()) {
-			if (file.isDirectory()) {
-				//默认读取主页
-				file = FileUtil.file(file, "index.html");
-			}
-			HandlerUtil.sendFile(httpExchange, file);
-		}
-
-		// 文件未找到
-		HandlerUtil.send404(httpExchange, null);
-	}
-}

+ 12 - 2
hutool-http/src/test/java/cn/hutool/http/server/SimpleServerTest.java

@@ -1,13 +1,23 @@
 package cn.hutool.http.server;
 
+import cn.hutool.http.ContentType;
 import cn.hutool.http.HttpUtil;
-import cn.hutool.http.server.handler.RootHandler;
 
 public class SimpleServerTest {
 
 	public static void main(String[] args) {
 		HttpUtil.createServer(8888)
-				.addHandler("/", new RootHandler("D:\\test"))
+				// 设置默认根目录,
+				.setRoot("d:/test")
+				// 返回JSON数据测试
+				.addAction("/restTest", (request, response) ->
+						response.write("{\"id\": 1, \"msg\": \"OK\"}", ContentType.JSON.toString())
+				)
+				// 获取表单数据测试
+				// http://localhost:8888/formTest?a=1&a=2&b=3
+				.addAction("/formTest", (request, response) ->
+						response.write(request.getParams().toString(), ContentType.TEXT_PLAIN.toString())
+				)
 				.start();
 	}
 }

+ 17 - 8
hutool-http/src/test/java/cn/hutool/http/test/HttpUtilTest.java

@@ -1,13 +1,5 @@
 package cn.hutool.http.test;
 
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.lang.Console;
 import cn.hutool.core.util.CharsetUtil;
@@ -15,6 +7,13 @@ import cn.hutool.core.util.ReUtil;
 import cn.hutool.http.Header;
 import cn.hutool.http.HttpRequest;
 import cn.hutool.http.HttpUtil;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
 
 public class HttpUtilTest {
 
@@ -164,6 +163,16 @@ public class HttpUtilTest {
 		paramsStr = "a=bbb&c=你好&哈喽&";
 		encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.CHARSET_UTF_8);
 		Assert.assertEquals("a=bbb&c=%E4%BD%A0%E5%A5%BD&%E5%93%88%E5%96%BD=", encode);
+
+		// URL原样输出
+		paramsStr = "https://www.hutool.cn/";
+		encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.CHARSET_UTF_8);
+		Assert.assertEquals(paramsStr, encode);
+
+		// URL原样输出
+		paramsStr = "https://www.hutool.cn/?";
+		encode = HttpUtil.encodeParams(paramsStr, CharsetUtil.CHARSET_UTF_8);
+		Assert.assertEquals("https://www.hutool.cn/", encode);
 	}
 
 	@Test

+ 1 - 1
hutool-json/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-json</artifactId>

+ 1 - 1
hutool-log/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-log</artifactId>

+ 1 - 1
hutool-poi/pom.xml

@@ -8,7 +8,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-poi</artifactId>

+ 1 - 1
hutool-script/pom.xml

@@ -8,7 +8,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-script</artifactId>

+ 1 - 1
hutool-setting/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-setting</artifactId>

+ 1 - 1
hutool-socket/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-socket</artifactId>

+ 1 - 1
hutool-system/pom.xml

@@ -9,7 +9,7 @@
 	<parent>
 		<groupId>cn.hutool</groupId>
 		<artifactId>hutool-parent</artifactId>
-		<version>5.2.6-SNAPSHOT</version>
+		<version>5.3.0-SNAPSHOT</version>
 	</parent>
 
 	<artifactId>hutool-system</artifactId>

+ 1 - 1
pom.xml

@@ -8,7 +8,7 @@
 
 	<groupId>cn.hutool</groupId>
 	<artifactId>hutool-parent</artifactId>
-	<version>5.2.6-SNAPSHOT</version>
+	<version>5.3.0-SNAPSHOT</version>
 	<name>hutool</name>
 	<description>提供丰富的Java工具方法</description>
 	<url>https://github.com/looly/hutool</url>