mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
add HttpResource
This commit is contained in:
parent
8fda1b9ac5
commit
f9e5625744
@ -3,10 +3,11 @@
|
||||
|
||||
-------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# 5.7.17 (2021-11-10)
|
||||
# 5.7.17 (2021-11-11)
|
||||
|
||||
### 🐣新特性
|
||||
* 【core 】 增加AsyncUtil(pr#457@Gitee)
|
||||
* 【http 】 增加HttpResource(issue#1943@Github)
|
||||
### 🐞Bug修复
|
||||
* 【core 】 修复FileResource构造fileName参数无效问题(issue#1942@Github)
|
||||
|
||||
|
@ -32,6 +32,7 @@ import java.io.PushbackInputStream;
|
||||
import java.io.PushbackReader;
|
||||
import java.io.Reader;
|
||||
import java.io.Serializable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.io.Writer;
|
||||
import java.nio.CharBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
@ -622,6 +623,9 @@ public class IoUtil extends NioUtil {
|
||||
if (in == null) {
|
||||
throw new IllegalArgumentException("The InputStream must not be null");
|
||||
}
|
||||
if(null != clazz){
|
||||
in.accept(clazz);
|
||||
}
|
||||
try {
|
||||
//noinspection unchecked
|
||||
return (T) in.readObject();
|
||||
@ -1331,4 +1335,19 @@ public class IoUtil extends NioUtil {
|
||||
public static LineIter lineIter(InputStream in, Charset charset) {
|
||||
return new LineIter(in, charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link ByteArrayOutputStream} 转换为String
|
||||
* @param out {@link ByteArrayOutputStream}
|
||||
* @param charset 编码
|
||||
* @return 字符串
|
||||
* @since 5.7.17
|
||||
*/
|
||||
public static String toStr(ByteArrayOutputStream out, Charset charset){
|
||||
try {
|
||||
return out.toString(charset.name());
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package cn.hutool.core.io.resource;
|
||||
import cn.hutool.core.io.IORuntimeException;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@ -59,7 +60,7 @@ public class CharSequenceResource implements Resource, Serializable {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name.toString();
|
||||
return StrUtil.str(this.name);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -39,7 +39,11 @@ public enum ContentType {
|
||||
/**
|
||||
* text/html编码
|
||||
*/
|
||||
TEXT_HTML("text/html");
|
||||
TEXT_HTML("text/html"),
|
||||
/**
|
||||
* application/octet-stream编码
|
||||
*/
|
||||
OCTET_STREAM("application/octet-stream");
|
||||
|
||||
private final String value;
|
||||
|
||||
|
56
hutool-http/src/main/java/cn/hutool/http/HttpResource.java
Normal file
56
hutool-http/src/main/java/cn/hutool/http/HttpResource.java
Normal file
@ -0,0 +1,56 @@
|
||||
package cn.hutool.http;
|
||||
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* HTTP资源,可自定义Content-Type
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.17
|
||||
*/
|
||||
public class HttpResource implements Resource, Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final Resource resource;
|
||||
private final String contentType;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param resource 资源,非空
|
||||
* @param contentType Content-Type类型,{@code null}表示不设置
|
||||
*/
|
||||
public HttpResource(Resource resource, String contentType) {
|
||||
this.resource = Assert.notNull(resource, "Resource must be not null !");
|
||||
this.contentType = contentType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return resource.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getUrl() {
|
||||
return resource.getUrl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getStream() {
|
||||
return resource.getStream();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取自定义Content-Type类型
|
||||
*
|
||||
* @return Content-Type类型
|
||||
*/
|
||||
public String getContentType() {
|
||||
return this.contentType;
|
||||
}
|
||||
}
|
@ -5,31 +5,33 @@ import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.resource.MultiResource;
|
||||
import cn.hutool.core.io.resource.Resource;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.RandomUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import cn.hutool.http.HttpResource;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Multipart/form-data数据的请求体封装
|
||||
* Multipart/form-data数据的请求体封装<br>
|
||||
* 遵循RFC2388规范
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.3.5
|
||||
*/
|
||||
public class MultipartBody implements RequestBody{
|
||||
public class MultipartBody implements RequestBody {
|
||||
|
||||
private static final String BOUNDARY = "--------------------Hutool_" + RandomUtil.randomString(16);
|
||||
private static final String BOUNDARY_END = StrUtil.format("--{}--\r\n", BOUNDARY);
|
||||
private static final String CONTENT_DISPOSITION_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"\r\n\r\n";
|
||||
private static final String CONTENT_DISPOSITION_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"\r\n";
|
||||
private static final String CONTENT_DISPOSITION_FILE_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"\r\n";
|
||||
|
||||
private static final String CONTENT_TYPE_MULTIPART_PREFIX = ContentType.MULTIPART.getValue() + "; boundary=";
|
||||
private static final String CONTENT_TYPE_FILE_TEMPLATE = "Content-Type: {}\r\n\r\n";
|
||||
private static final String CONTENT_TYPE_FILE_TEMPLATE = "Content-Type: {}\r\n";
|
||||
|
||||
/**
|
||||
* 存储表单数据
|
||||
@ -42,11 +44,12 @@ public class MultipartBody implements RequestBody{
|
||||
|
||||
/**
|
||||
* 根据已有表单内容,构建MultipartBody
|
||||
* @param form 表单
|
||||
*
|
||||
* @param form 表单
|
||||
* @param charset 编码
|
||||
* @return MultipartBody
|
||||
*/
|
||||
public static MultipartBody create(Map<String, Object> form, Charset charset){
|
||||
public static MultipartBody create(Map<String, Object> form, Charset charset) {
|
||||
return new MultipartBody(form, charset);
|
||||
}
|
||||
|
||||
@ -55,15 +58,15 @@ public class MultipartBody implements RequestBody{
|
||||
*
|
||||
* @return Multipart的Content-Type类型
|
||||
*/
|
||||
public static String getContentType(){
|
||||
public static String getContentType() {
|
||||
return CONTENT_TYPE_MULTIPART_PREFIX + BOUNDARY;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param form 表单
|
||||
* @param charset 编码
|
||||
* @param form 表单
|
||||
* @param charset 编码
|
||||
*/
|
||||
public MultipartBody(Map<String, Object> form, Charset charset) {
|
||||
this.form = form;
|
||||
@ -81,6 +84,13 @@ public class MultipartBody implements RequestBody{
|
||||
formEnd(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
write(out);
|
||||
return IoUtil.toStr(out, this.charset);
|
||||
}
|
||||
|
||||
// 普通字符串数据
|
||||
|
||||
/**
|
||||
@ -97,10 +107,26 @@ public class MultipartBody implements RequestBody{
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Multipart表单的数据项
|
||||
* 添加Multipart表单的数据项<br>
|
||||
* <pre>
|
||||
* --分隔符(boundary)[换行]
|
||||
* Content-Disposition: form-data; name="参数名"[换行]
|
||||
* [换行]
|
||||
* 参数值[换行]
|
||||
* </pre>
|
||||
*
|
||||
* 或者:
|
||||
*
|
||||
* <pre>
|
||||
* --分隔符(boundary)[换行]
|
||||
* Content-Disposition: form-data; name="表单名"; filename="文件名"[换行]
|
||||
* Content-Type: MIME类型[换行]
|
||||
* [换行]
|
||||
* 文件的二进制内容[换行]
|
||||
* </pre>
|
||||
*
|
||||
* @param formFieldName 表单名
|
||||
* @param value 值,可以是普通值、资源(如文件等)
|
||||
* @param value 值,可以是普通值、资源(如文件等)
|
||||
* @param out Http流
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
@ -113,25 +139,63 @@ public class MultipartBody implements RequestBody{
|
||||
return;
|
||||
}
|
||||
|
||||
// --分隔符(boundary)[换行]
|
||||
write(out, "--", BOUNDARY, StrUtil.CRLF);
|
||||
|
||||
if(value instanceof Resource){
|
||||
// 文件资源(二进制资源)
|
||||
final Resource resource = (Resource)value;
|
||||
final String fileName = resource.getName();
|
||||
write(out, StrUtil.format(CONTENT_DISPOSITION_FILE_TEMPLATE, formFieldName, ObjectUtil.defaultIfNull(fileName, formFieldName)));
|
||||
// 根据name的扩展名指定互联网媒体类型,默认二进制流数据
|
||||
write(out, StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE, HttpUtil.getMimeType(fileName, "application/octet-stream")));
|
||||
resource.writeTo(out);
|
||||
} else{
|
||||
// 普通数据
|
||||
if (value instanceof Resource) {
|
||||
appendResource(formFieldName, (Resource) value, out);
|
||||
} else {
|
||||
/*
|
||||
* Content-Disposition: form-data; name="参数名"[换行]
|
||||
* [换行]
|
||||
* 参数值
|
||||
*/
|
||||
write(out, StrUtil.format(CONTENT_DISPOSITION_TEMPLATE, formFieldName));
|
||||
write(out, StrUtil.CRLF);
|
||||
write(out, value);
|
||||
}
|
||||
|
||||
write(out, StrUtil.CRLF);
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加Multipart表单的Resource数据项,支持包括{@link HttpResource}资源格式
|
||||
*
|
||||
* @param formFieldName 表单名
|
||||
* @param resource 资源
|
||||
* @param out Http流
|
||||
* @throws IORuntimeException IO异常
|
||||
*/
|
||||
private void appendResource(String formFieldName, Resource resource, OutputStream out) throws IORuntimeException {
|
||||
final String fileName = resource.getName();
|
||||
|
||||
// Content-Disposition
|
||||
if (null == fileName) {
|
||||
// Content-Disposition: form-data; name="参数名"[换行]
|
||||
write(out, StrUtil.format(CONTENT_DISPOSITION_TEMPLATE, formFieldName));
|
||||
} else {
|
||||
// Content-Disposition: form-data; name="参数名"; filename="文件名"[换行]
|
||||
write(out, StrUtil.format(CONTENT_DISPOSITION_FILE_TEMPLATE, formFieldName, fileName));
|
||||
}
|
||||
|
||||
// Content-Type
|
||||
if (resource instanceof HttpResource) {
|
||||
final String contentType = ((HttpResource) resource).getContentType();
|
||||
if (StrUtil.isNotBlank(contentType)) {
|
||||
// Content-Type: 类型[换行]
|
||||
write(out, StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE, contentType));
|
||||
}
|
||||
} else {
|
||||
// 根据name的扩展名指定互联网媒体类型,默认二进制流数据
|
||||
write(out, StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE,
|
||||
HttpUtil.getMimeType(fileName, ContentType.OCTET_STREAM.getValue())));
|
||||
}
|
||||
|
||||
// 内容
|
||||
write(out, "\r\n");
|
||||
resource.writeTo(out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传表单结束
|
||||
*
|
||||
|
@ -0,0 +1,27 @@
|
||||
package cn.hutool.http.body;
|
||||
|
||||
import cn.hutool.core.io.resource.StringResource;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.http.HttpResource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MultipartBodyTest {
|
||||
|
||||
@Test
|
||||
public void buildTest(){
|
||||
Map<String, Object> form = new HashMap<>();
|
||||
form.put("pic1", "pic1 content");
|
||||
form.put("pic2", new HttpResource(
|
||||
new StringResource("pic2 content"), "text/plain"));
|
||||
form.put("pic3", new HttpResource(
|
||||
new StringResource("pic3 content", "pic3.jpg"), "image/jpeg"));
|
||||
|
||||
final MultipartBody body = MultipartBody.create(form, CharsetUtil.CHARSET_UTF_8);
|
||||
|
||||
Assert.assertNotNull(body.toString());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user