mirror of
https://gitee.com/dromara/hutool.git
synced 2025-04-05 17:37:59 +08:00
prepare 5.3.0
This commit is contained in:
parent
3921a568dd
commit
6b13cb5263
@ -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)
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
10
README.md
10
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 @@
|
||||
5.2.6
|
||||
5.3.0
|
||||
|
@ -1 +1 @@
|
||||
var version = '5.2.6'
|
||||
var version = '5.3.0'
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
hutool-core/src/main/java/cn/hutool/core/map/BiMap.java
Normal file
71
hutool-core/src/main/java/cn/hutool/core/map/BiMap.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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>() {
|
||||
@ -707,6 +710,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排序方式(字母顺序)
|
||||
*
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
@ -48,16 +47,6 @@ public class MultipartFormData {
|
||||
}
|
||||
// --------------------------------------------------------------------- Constructor end
|
||||
|
||||
/**
|
||||
* 解析上传文件和表单数据
|
||||
*
|
||||
* @param request Http请求
|
||||
* @throws IOException IO异常
|
||||
*/
|
||||
public void parseRequest(ServletRequest request) throws IOException {
|
||||
parseRequestStream(request.getInputStream(), request.getCharacterEncoding());
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取上传的文件和表单数据
|
||||
*
|
||||
@ -65,7 +54,7 @@ public class MultipartFormData {
|
||||
* @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);
|
@ -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 读取异常
|
||||
*/
|
@ -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,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;
|
@ -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) {
|
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* 文件上传封装
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.core.net.multipart;
|
@ -62,6 +62,17 @@ public class CharsetUtil {
|
||||
return StrUtil.isBlank(charsetName) ? Charset.defaultCharset() : Charset.forName(charsetName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析字符串编码为Charset对象,解析失败返回系统默认编码
|
||||
*
|
||||
* @param charsetName 字符集,为空则返回默认字符集
|
||||
* @return Charset
|
||||
* @since 5.2.6
|
||||
*/
|
||||
public static Charset parse(String charsetName) {
|
||||
return parse(charsetName, Charset.defaultCharset());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析字符串编码为Charset对象,解析失败返回默认编码
|
||||
*
|
||||
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
/**
|
||||
* 基于Servlet的文件上传封装
|
||||
*
|
||||
* @author looly
|
||||
*
|
||||
*/
|
||||
package cn.hutool.extra.servlet.multipart;
|
@ -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>
|
||||
|
@ -28,10 +28,18 @@ public enum ContentType {
|
||||
* Rest请求XML编码
|
||||
*/
|
||||
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());
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求体的流,流中可以读取请求内容,包括请求表单数据或文件上传数据
|
||||
*
|
||||
* @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));
|
||||
}
|
||||
}
|
||||
|
||||
final String contentType = getContentType();
|
||||
if (StrUtil.isBlank(contentType)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
@ -48,6 +56,38 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
return send(httpStatusCode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送成功状态码
|
||||
*
|
||||
* @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状态码
|
||||
*
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,12 +218,27 @@ public class HttpServerResponse extends HttpServerBase {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置属性
|
||||
*
|
||||
* @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);
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 !");
|
||||
}
|
||||
}
|
@ -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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user