All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.zhangxd1989.basetool.http.HttpRequest Maven / Gradle / Ivy

There is a newer version: 1.0.16
Show newest version
package com.github.zhangxd1989.basetool.http;

import com.github.zhangxd1989.basetool.codec.Base64;
import com.github.zhangxd1989.basetool.collection.CollectionUtil;
import com.github.zhangxd1989.basetool.convert.Convert;
import com.github.zhangxd1989.basetool.http.cookie.GlobalCookieManager;
import com.github.zhangxd1989.basetool.http.ssl.SSLSocketFactoryBuilder;
import com.github.zhangxd1989.basetool.io.IoUtil;
import com.github.zhangxd1989.basetool.io.resource.BytesResource;
import com.github.zhangxd1989.basetool.io.resource.FileResource;
import com.github.zhangxd1989.basetool.io.resource.MultiFileResource;
import com.github.zhangxd1989.basetool.io.resource.MultiResource;
import com.github.zhangxd1989.basetool.io.resource.Resource;
import com.github.zhangxd1989.basetool.json.JSON;
import com.github.zhangxd1989.basetool.lang.Assert;
import com.github.zhangxd1989.basetool.log.StaticLog;
import com.github.zhangxd1989.basetool.map.MapUtil;
import com.github.zhangxd1989.basetool.util.ArrayUtil;
import com.github.zhangxd1989.basetool.util.CharsetUtil;
import com.github.zhangxd1989.basetool.util.ObjectUtil;
import com.github.zhangxd1989.basetool.util.RandomUtil;
import com.github.zhangxd1989.basetool.util.StrUtil;
import com.github.zhangxd1989.basetool.util.URLUtil;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.CookieManager;
import java.net.HttpCookie;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;


/**
 * http请求类
* Http请求类用于构建Http请求并同步获取结果,此类通过CookieManager持有域名对应的Cookie值,再次请求时会自动附带Cookie信息 * * @author sheldon */ public class HttpRequest extends HttpBase { /** * 默认超时时长,-1表示默认超时时长 */ public static final int TIMEOUT_DEFAULT = -1; private static final String BOUNDARY = "--------------------basetool_" + RandomUtil.randomString(16); private static final byte[] BOUNDARY_END = StrUtil.format("--{}--\r\n", BOUNDARY).getBytes(CharsetUtil.CHARSET_UTF_8); private static final String CONTENT_DISPOSITION_TEMPLATE = "Content-Disposition: form-data; name=\"{}\"\r\n\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 = "multipart/form-data; boundary="; private static final String CONTENT_TYPE_FILE_TEMPLATE = "Content-Type: {}\r\n\r\n"; /** * 获取Cookie管理器,用于自定义Cookie管理 * * @return {@link CookieManager} * @see GlobalCookieManager#getCookieManager() */ public static CookieManager getCookieManager() { return GlobalCookieManager.getCookieManager(); } /** * 自定义{@link CookieManager} * * @param customCookieManager 自定义的{@link CookieManager} * @see GlobalCookieManager#setCookieManager(CookieManager) */ public static void setCookieManager(CookieManager customCookieManager) { GlobalCookieManager.setCookieManager(customCookieManager); } /** * 关闭Cookie * * @see GlobalCookieManager#setCookieManager(CookieManager) */ public static void closeCookie() { GlobalCookieManager.setCookieManager(null); } private String url; private URLStreamHandler urlHandler; private Method method = Method.GET; /** * 默认连接超时 */ private int connectionTimeout = TIMEOUT_DEFAULT; /** * 默认读取超时 */ private int readTimeout = TIMEOUT_DEFAULT; /** * 存储表单数据 */ private Map form; /** * 文件表单对象,用于文件上传 */ private Map fileForm; /** * Cookie */ private String cookie; /** * 连接对象 */ private HttpConnection httpConnection; /** * 是否禁用缓存 */ private boolean isDisableCache; /** * 是否对url中的参数进行编码 */ private boolean encodeUrlParams; /** * 是否是REST请求模式 */ private boolean isRest; /** * 重定向次数计数器,内部使用 */ private int redirectCount; /** * 最大重定向次数 */ private int maxRedirectCount; /** * 代理 */ private Proxy proxy; /** * HostnameVerifier,用于HTTPS安全连接 */ private HostnameVerifier hostnameVerifier; /** * SSLSocketFactory,用于HTTPS安全连接 */ private SSLSocketFactory ssf; /** * 构造 * * @param url URL */ public HttpRequest(String url) { Assert.notBlank(url, "Param [url] can not be blank !"); this.url = URLUtil.normalize(url, true); // 给定一个默认头信息 this.header(GlobalHeaders.INSTANCE.headers); } /** * POST请求 * * @param url URL * @return HttpRequest */ public static HttpRequest post(String url) { return new HttpRequest(url).method(Method.POST); } /** * GET请求 * * @param url URL * @return HttpRequest */ public static HttpRequest get(String url) { return new HttpRequest(url).method(Method.GET); } /** * HEAD请求 * * @param url URL * @return HttpRequest */ public static HttpRequest head(String url) { return new HttpRequest(url).method(Method.HEAD); } /** * OPTIONS请求 * * @param url URL * @return HttpRequest */ public static HttpRequest options(String url) { return new HttpRequest(url).method(Method.OPTIONS); } /** * PUT请求 * * @param url URL * @return HttpRequest */ public static HttpRequest put(String url) { return new HttpRequest(url).method(Method.PUT); } /** * PATCH请求 * * @param url URL * @return HttpRequest */ public static HttpRequest patch(String url) { return new HttpRequest(url).method(Method.PATCH); } /** * DELETE请求 * * @param url URL * @return HttpRequest */ public static HttpRequest delete(String url) { return new HttpRequest(url).method(Method.DELETE); } /** * TRACE请求 * * @param url URL * @return HttpRequest */ public static HttpRequest trace(String url) { return new HttpRequest(url).method(Method.TRACE); } /** * 获取请求URL * * @return URL字符串 */ public String getUrl() { return url; } /** * 设置URL * * @param url url字符串 * @return HttpRequest */ public HttpRequest setUrl(String url) { this.url = url; return this; } /** * 设置{@link URLStreamHandler} *

* 部分环境下需要单独设置此项,例如当 WebLogic Server 实例充当 SSL 客户端角色(它会尝试通过 SSL 连接到其他服务器或应用程序)时,
* 它会验证 SSL 服务器在数字证书中返回的主机名是否与用于连接 SSL 服务器的 URL 主机名相匹配。如果主机名不匹配,则删除此连接。
* 因此weblogic不支持https的sni协议的主机名验证,此时需要将此值设置为sun.net.www.protocol.https.Handler对象。 * * @param urlHandler {@link URLStreamHandler} * @return HttpRequest */ public HttpRequest setUrlHandler(URLStreamHandler urlHandler) { this.urlHandler = urlHandler; return this; } /** * 获取Http请求方法 * * @return {@link Method} */ public Method getMethod() { return this.method; } /** * 设置请求方法 * * @param method HTTP方法 * @return HttpRequest * @see #method(Method) */ public HttpRequest setMethod(Method method) { return method(method); } /** * 获取{@link HttpConnection} * * @return {@link HttpConnection} */ public HttpConnection getConnection() { return this.httpConnection; } /** * 设置请求方法 * * @param method HTTP方法 * @return HttpRequest */ public HttpRequest method(Method method) { if (Method.PATCH == method) { this.method = Method.POST; this.header("X-HTTP-Method-Override", "PATCH"); } else { this.method = method; } return this; } /** * 设置contentType * * @param contentType contentType * @return HttpRequest */ public HttpRequest contentType(String contentType) { header(Header.CONTENT_TYPE, contentType); return this; } /** * 设置是否为长连接 * * @param isKeepAlive 是否长连接 * @return HttpRequest */ public HttpRequest keepAlive(boolean isKeepAlive) { header(Header.CONNECTION, isKeepAlive ? "Keep-Alive" : "Close"); return this; } /** * @return 获取是否为长连接 */ public boolean isKeepAlive() { String connection = header(Header.CONNECTION); if (connection == null) { return !httpVersion.equalsIgnoreCase(HTTP_1_0); } return !"close".equalsIgnoreCase(connection); } /** * 获取内容长度 * * @return String */ public String contentLength() { return header(Header.CONTENT_LENGTH); } /** * 设置内容长度 * * @param value 长度 * @return HttpRequest */ public HttpRequest contentLength(int value) { header(Header.CONTENT_LENGTH, String.valueOf(value)); return this; } /** * 设置Cookie
* 自定义Cookie后会覆盖basetool的默认Cookie行为 * * @param cookies Cookie值数组,如果为{@code null}则设置无效,使用默认Cookie行为 * @return this */ public HttpRequest cookie(HttpCookie... cookies) { if (ArrayUtil.isEmpty(cookies)) { return disableCookie(); } return cookie(ArrayUtil.join(cookies, ";")); } /** * 设置Cookie
* 自定义Cookie后会覆盖basetool的默认Cookie行为 * * @param cookie Cookie值,如果为{@code null}则设置无效,使用默认Cookie行为 * @return this */ public HttpRequest cookie(String cookie) { this.cookie = cookie; return this; } /** * 禁用默认Cookie行为,此方法调用后会将Cookie置为空。
* 如果想重新启用Cookie,请调用:{@link #cookie(String)}方法自定义Cookie。
* 如果想启动默认的Cookie行为(自动回填服务器传回的Cookie),则调用{@link #enableDefaultCookie()} * * @return this */ public HttpRequest disableCookie() { return cookie(StrUtil.EMPTY); } /** * 打开默认的Cookie行为(自动回填服务器传回的Cookie) * * @return this */ public HttpRequest enableDefaultCookie() { return cookie((String) null); } /** * 设置表单数据
* * @param name 名 * @param value 值 * @return this */ public HttpRequest form(String name, Object value) { if (StrUtil.isBlank(name) || ObjectUtil.isNull(value)) { return this; // 忽略非法的form表单项内容; } // 停用body this.bodyBytes = null; if (value instanceof File) { // 文件上传 return this.form(name, (File) value); } else if (value instanceof Resource) { // 自定义流上传 return this.form(name, (Resource) value); } else if (this.form == null) { this.form = new LinkedHashMap<>(); } String strValue; if (value instanceof List) { // 列表对象 strValue = CollectionUtil.join((List) value, ","); } else if (ArrayUtil.isArray(value)) { if (File.class == ArrayUtil.getComponentType(value)) { // 多文件 return this.form(name, (File[]) value); } // 数组对象 strValue = ArrayUtil.join((Object[]) value, ","); } else { // 其他对象一律转换为字符串 strValue = Convert.toStr(value, null); } form.put(name, strValue); return this; } /** * 设置表单数据 * * @param name 名 * @param value 值 * @param parameters 参数对,奇数为名,偶数为值 * @return this */ public HttpRequest form(String name, Object value, Object... parameters) { form(name, value); for (int i = 0; i < parameters.length; i += 2) { name = parameters[i].toString(); form(name, parameters[i + 1]); } return this; } /** * 设置map类型表单数据 * * @param formMap 表单内容 * @return this */ public HttpRequest form(Map formMap) { if (MapUtil.isNotEmpty(formMap)) { for (Entry entry : formMap.entrySet()) { form(entry.getKey(), entry.getValue()); } } return this; } /** * 文件表单项
* 一旦有文件加入,表单变为multipart/form-data * * @param name 名 * @param files 需要上传的文件 * @return this */ public HttpRequest form(String name, File... files) { if (1 == files.length) { final File file = files[0]; return form(name, file, file.getName()); } return form(name, new MultiFileResource(files)); } /** * 文件表单项
* 一旦有文件加入,表单变为multipart/form-data * * @param name 名 * @param file 需要上传的文件 * @return this */ public HttpRequest form(String name, File file) { return form(name, file, file.getName()); } /** * 文件表单项
* 一旦有文件加入,表单变为multipart/form-data * * @param name 名 * @param file 需要上传的文件 * @param fileName 文件名,为空使用文件默认的文件名 * @return this */ public HttpRequest form(String name, File file, String fileName) { if (null != file) { form(name, new FileResource(file, fileName)); } return this; } /** * 文件byte[]表单项
* 一旦有文件加入,表单变为multipart/form-data * * @param name 名 * @param fileBytes 需要上传的文件 * @param fileName 文件名 * @return this */ public HttpRequest form(String name, byte[] fileBytes, String fileName) { if (null != fileBytes) { form(name, new BytesResource(fileBytes, fileName)); } return this; } /** * 文件表单项
* 一旦有文件加入,表单变为multipart/form-data * * @param name 名 * @param resource 数据源,文件可以使用{@link FileResource}包装使用 * @return this */ public HttpRequest form(String name, Resource resource) { if (null != resource) { if (!isKeepAlive()) { keepAlive(true); } if (null == this.fileForm) { fileForm = new HashMap<>(); } // 文件对象 this.fileForm.put(name, resource); } return this; } /** * 获取表单数据 * * @return 表单Map */ public Map form() { return this.form; } /** * 获取文件表单数据 * * @return 文件表单Map */ public Map fileForm() { return this.fileForm; } /** * 设置内容主体 * * @param body 请求体 * @return this */ public HttpRequest body(String body) { return this.body(body, null); } /** * 设置内容主体
* 请求体body参数支持两种类型: *

     * 1. 标准参数,例如 a=1&b=2 这种格式
     * 2. Rest模式,此时body需要传入一个JSON或者XML字符串,basetool会自动绑定其对应的Content-Type
     * 
* * @param body 请求体 * @param contentType 请求体类型,{@code null}表示自动判断类型 * @return this */ public HttpRequest body(String body, String contentType) { body(StrUtil.bytes(body, this.charset)); this.form = null; // 当使用body时,停止form的使用 contentLength((null != body ? body.length() : 0)); if (null != contentType) { // Content-Type自定义设置 this.contentType(contentType); } else { // 在用户未自定义的情况下自动根据内容判断 contentType = HttpUtil.getContentTypeByRequestBody(body); if (null != contentType && ContentType.isDefault(this.header(Header.CONTENT_TYPE))) { if (null != this.charset) { // 附加编码信息 contentType = ContentType.build(contentType, this.charset); } this.contentType(contentType); } } // 判断是否为rest请求 if (StrUtil.containsAnyIgnoreCase(contentType, "json", "xml")) { this.isRest = true; } return this; } /** * 设置JSON内容主体
* 设置默认的Content-Type为 application/json 需在此方法调用前使用charset方法设置编码,否则使用默认编码UTF-8 * * @param json JSON请求体 * @return this */ public HttpRequest body(JSON json) { return this.body(json.toString()); } /** * 设置主体字节码
* 需在此方法调用前使用charset方法设置编码,否则使用默认编码UTF-8 * * @param bodyBytes 主体 * @return this */ public HttpRequest body(byte[] bodyBytes) { if (bodyBytes == null) { this.bodyBytes = null; } else { this.bodyBytes = bodyBytes.clone(); } return this; } /** * 设置超时,单位:毫秒
* 超时包括: * *
     * 1. 连接超时
     * 2. 读取响应超时
     * 
* * @param milliseconds 超时毫秒数 * @return this * @see #setConnectionTimeout(int) * @see #setReadTimeout(int) */ public HttpRequest timeout(int milliseconds) { setConnectionTimeout(milliseconds); setReadTimeout(milliseconds); return this; } /** * 设置连接超时,单位:毫秒 * * @param milliseconds 超时毫秒数 * @return this */ public HttpRequest setConnectionTimeout(int milliseconds) { this.connectionTimeout = milliseconds; return this; } /** * 设置连接超时,单位:毫秒 * * @param milliseconds 超时毫秒数 * @return this */ public HttpRequest setReadTimeout(int milliseconds) { this.readTimeout = milliseconds; return this; } /** * 禁用缓存 * * @return this */ public HttpRequest disableCache() { this.isDisableCache = true; return this; } /** * 是否对URL中的参数进行编码 * * @param isEncodeUrlParams 是否对URL中的参数进行编码 * @return this */ public HttpRequest setEncodeUrlParams(boolean isEncodeUrlParams) { this.encodeUrlParams = isEncodeUrlParams; return this; } /** * 设置是否打开重定向,如果打开默认重定向次数为2
* 此方法效果与{@link #setMaxRedirectCount(int)} 一致 * * @param isFollowRedirects 是否打开重定向 * @return this */ public HttpRequest setFollowRedirects(boolean isFollowRedirects) { return setMaxRedirectCount(isFollowRedirects ? 2 : 0); } /** * 设置最大重定向次数
* 如果次数小于1则表示不重定向,大于等于1表示打开重定向 * * @param maxRedirectCount 最大重定向次数 * @return this */ public HttpRequest setMaxRedirectCount(int maxRedirectCount) { if (maxRedirectCount > 0) { this.maxRedirectCount = maxRedirectCount; } else { this.maxRedirectCount = 0; } return this; } /** * 设置域名验证器
* 只针对HTTPS请求,如果不设置,不做验证,所有域名被信任 * * @param hostnameVerifier HostnameVerifier * @return this */ public HttpRequest setHostnameVerifier(HostnameVerifier hostnameVerifier) { // 验证域 this.hostnameVerifier = hostnameVerifier; return this; } /** * 设置代理 * * @param proxy 代理 {@link Proxy} * @return this */ public HttpRequest setProxy(Proxy proxy) { this.proxy = proxy; return this; } /** * 设置SSLSocketFactory
* 只针对HTTPS请求,如果不设置,使用默认的SSLSocketFactory
* 默认SSLSocketFactory为:SSLSocketFactoryBuilder.create().build(); * * @param ssf SSLScketFactory * @return this */ public HttpRequest setSSLSocketFactory(SSLSocketFactory ssf) { this.ssf = ssf; return this; } /** * 设置HTTPS安全连接协议,只针对HTTPS请求,可以使用的协议包括:
*
     * 1. TLS_V_1.2
     * 2. TLS_V_1.1
     * 3. SSL_V_3
     * ...
     * 
* * @param protocol 协议 * @return this * @see SSLSocketFactoryBuilder */ public HttpRequest setSSLProtocol(String protocol) { if (null == this.ssf) { try { this.ssf = SSLSocketFactoryBuilder.create().setProtocol(protocol).build(); } catch (Exception e) { throw new HttpException(e); } } return this; } /** * 设置是否rest模式 * * @param isRest 是否rest模式 * @return this */ public HttpRequest setRest(boolean isRest) { this.isRest = isRest; return this; } /** * 执行Reuqest请求 * * @return this */ public HttpResponse execute() { return this.execute(false); } /** * 异步请求
* 异步请求后获取的{@link HttpResponse} 为异步模式,此时此对象持有Http链接(http链接并不会关闭),直调用获取内容方法为止 * * @return 异步对象,使用get方法获取HttpResponse对象 */ public HttpResponse executeAsync() { return this.execute(true); } /** * 执行Reuqest请求 * * @param isAsync 是否异步 * @return this */ public HttpResponse execute(boolean isAsync) { // 初始化URL urlWithParamIfGet(); //编码URL if (this.encodeUrlParams) { this.url = HttpUtil.encodeParams(this.url, this.charset); } // 初始化 connection initConnection(); // 发送请求 send(); // 手动实现重定向 HttpResponse httpResponse = sendRedirectIfPossible(); // 获取响应 if (null == httpResponse) { httpResponse = new HttpResponse(this.httpConnection, this.charset, isAsync, isIgnoreResponseBody()); } return httpResponse; } /** * 简单验证 * * @param username 用户名 * @param password 密码 * @return HttpRequest */ public HttpRequest basicAuth(String username, String password) { final String data = username.concat(":").concat(password); final String base64 = Base64.encode(data, charset); header("Authorization", "Basic " + base64, true); return this; } /** * 初始化网络连接 */ private void initConnection() { this.httpConnection = HttpConnection.create(URLUtil.toUrlForHttp(this.url, this.urlHandler), this.proxy) .setMethod(this.method) .setHttpsInfo(this.hostnameVerifier, this.ssf) .setConnectTimeout(this.connectionTimeout) .setReadTimeout(this.readTimeout) // 自定义Cookie .setCookie(this.cookie) // 定义转发 .setInstanceFollowRedirects(this.maxRedirectCount > 0) // 覆盖默认Header .header(this.headers, true); // 读取全局Cookie信息并附带到请求中 GlobalCookieManager.add(this.httpConnection); // 是否禁用缓存 if (this.isDisableCache) { this.httpConnection.disableCache(); } } /** * 对于GET请求将参数加到URL中
* 此处不对URL中的特殊字符做单独编码 */ private void urlWithParamIfGet() { if (Method.GET.equals(method) && !this.isRest) { // 优先使用body形式的参数,不存在使用form if (ArrayUtil.isNotEmpty(this.bodyBytes)) { this.url = HttpUtil.urlWithForm(this.url, StrUtil.str(this.bodyBytes, this.charset), this.charset, false); } else { this.url = HttpUtil.urlWithForm(this.url, this.form, this.charset, false); } } } /** * 调用转发,如果需要转发返回转发结果,否则返回null * * @return {@link HttpResponse},无转发返回 null */ private HttpResponse sendRedirectIfPossible() { if (this.maxRedirectCount < 1) { // 不重定向 return null; } // 手动实现重定向 if (this.httpConnection.getHttpURLConnection().getInstanceFollowRedirects()) { int responseCode; try { responseCode = httpConnection.responseCode(); } catch (IOException e) { throw new HttpException(e); } if (responseCode != HttpURLConnection.HTTP_OK) { if (responseCode == HttpURLConnection.HTTP_MOVED_TEMP || responseCode == HttpURLConnection.HTTP_MOVED_PERM || responseCode == HttpURLConnection.HTTP_SEE_OTHER) { this.url = httpConnection.header(Header.LOCATION); if (redirectCount < this.maxRedirectCount) { redirectCount++; return execute(); } else { StaticLog.warn("URL [{}] redirect count more than two !", this.url); } } } } return null; } /** * 发送数据流 * * @throws HttpException */ private void send() throws HttpException { try { if (Method.POST.equals(this.method) || Method.PUT.equals(this.method) || Method.DELETE.equals(this.method) || this.isRest) { if (CollectionUtil.isEmpty(this.fileForm)) { sendFormUrlEncoded();// 普通表单 } else { sendMultipart(); // 文件上传表单 } } else { this.httpConnection.connect(); } } catch (IOException e) { throw new HttpException(e.getMessage(), e); } } /** * 发送普通表单 * * @throws IOException */ private void sendFormUrlEncoded() throws IOException { if (StrUtil.isBlank(this.header(Header.CONTENT_TYPE))) { // 如果未自定义Content-Type,使用默认的application/x-www-form-urlencoded this.httpConnection.header(Header.CONTENT_TYPE, ContentType.FORM_URLENCODED.toString(this.charset), true); } // Write的时候会优先使用body中的内容,write时自动关闭OutputStream if (ArrayUtil.isNotEmpty(this.bodyBytes)) { IoUtil.write(this.httpConnection.getOutputStream(), true, this.bodyBytes); } else { final String content = HttpUtil.toParams(this.form, this.charset); IoUtil.write(this.httpConnection.getOutputStream(), this.charset, true, content); } } /** * 发送多组件请求(例如包含文件的表单) * * @throws IOException */ private void sendMultipart() throws IOException { setMultipart();// 设置表单类型为Multipart final OutputStream out = this.httpConnection.getOutputStream(); try { writeFileForm(out); writeForm(out); formEnd(out); } finally { IoUtil.close(out); } } // 普通字符串数据 /** * 发送普通表单内容 * * @param out 输出流 * @throws IOException */ private void writeForm(OutputStream out) throws IOException { if (CollectionUtil.isNotEmpty(this.form)) { StringBuilder builder = StrUtil.builder(); for (Entry entry : this.form.entrySet()) { builder.append("--").append(BOUNDARY).append(StrUtil.CRLF); builder.append(StrUtil.format(CONTENT_DISPOSITION_TEMPLATE, entry.getKey())); builder.append(entry.getValue()).append(StrUtil.CRLF); } IoUtil.write(out, this.charset, false, builder); } } /** * 发送文件对象表单 * * @param out 输出流 * @throws IOException */ private void writeFileForm(OutputStream out) throws IOException { for (Entry entry : this.fileForm.entrySet()) { appendPart(entry.getKey(), entry.getValue(), out); } } /** * 添加Multipart表单的数据项 * * @param formFieldName 表单名 * @param resource 资源,可以是文件等 * @param out Http流 */ private void appendPart(String formFieldName, Resource resource, OutputStream out) { if (resource instanceof MultiResource) { // 多资源 for (Resource subResource : (MultiResource) resource) { appendPart(formFieldName, subResource, out); } } else { // 普通资源 final StringBuilder builder = StrUtil.builder().append("--").append(BOUNDARY).append(StrUtil.CRLF); final String fileName = resource.getName(); builder.append(StrUtil.format(CONTENT_DISPOSITION_FILE_TEMPLATE, formFieldName, ObjectUtil.defaultIfNull(fileName, formFieldName))); builder.append(StrUtil.format(CONTENT_TYPE_FILE_TEMPLATE, HttpUtil.getMimeType(fileName))); IoUtil.write(out, this.charset, false, builder); InputStream in = null; try { in = resource.getStream(); IoUtil.copy(in, out); } finally { IoUtil.close(in); } IoUtil.write(out, this.charset, false, StrUtil.CRLF); } } /** * 上传表单结束 * * @param out 输出流 * @throws IOException */ private void formEnd(OutputStream out) throws IOException { out.write(BOUNDARY_END); out.flush(); } /** * 设置表单类型为Multipart(文件上传) * * @return HttpConnection */ private void setMultipart() { this.httpConnection.header(Header.CONTENT_TYPE, CONTENT_TYPE_MULTIPART_PREFIX + BOUNDARY, true); } /** * 是否忽略读取响应body部分
* HEAD、CONNECT、OPTIONS、TRACE方法将不读取响应体 * * @return 是否需要忽略响应body部分 */ private boolean isIgnoreResponseBody() { if (Method.HEAD == this.method || Method.CONNECT == this.method || Method.OPTIONS == this.method || Method.TRACE == this.method) { return true; } return false; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy