代码
package awesome.data.structure.http;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* http 工具类
*
* @author: Andy
* @time: 2019/7/9 17:09
* @since
*/
public class HttpUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);
/**
* multipart/form-data 格式发送数据时各个部分分隔符的前缀,必须为 --
*/
private static final String BOUNDARY_PREFIX = "--";
/**
* 回车换行,用于一行的结尾
*/
private static final String LINE_END = "\r\n";
/**
* post 请求:以表单方式提交数据
* <p>
* 由于 multipart/form-data 不是 http 标准内容,而是属于扩展类型,
* 因此需要自己构造数据结构,具体如下:
* <p>
* 1、首先,设置 Content-Type
* <p>
* Content-Type: multipart/form-data; boundary=${bound}
* <p>
* 其中${bound} 是一个占位符,代表我们规定的分割符,可以自己任意规定,
* 但为了避免和正常文本重复了,尽量要使用复杂一点的内容
* <p>
* 2、设置主体内容
* <p>
* --${bound}
* Content-Disposition: form-data; name="userName"
* <p>
* Andy
* --${bound}
* Content-Disposition: form-data; name="file"; filename="测试.excel"
* Content-Type: application/octet-stream
* <p>
* 文件内容
* --${bound}--
* <p>
* 其中${bound}是之前头信息中的分隔符,如果头信息中规定是123,那这里也要是123;
* 可以很容易看到,这个请求提是多个相同部分组成的:
* 每一部分都是以--加分隔符开始的,然后是该部分内容的描述信息,然后一个回车换行,然后是描述信息的具体内容;
* 如果传送的内容是一个文件的话,那么还会包含文件名信息以及文件内容类型。
* 上面第二部分是一个文件体的结构,最后以--分隔符--结尾,表示请求体结束
*
* @param urlStr 请求的url
* @param filePathMap key 参数名,value 文件的路径
* @param keyValues 普通参数的键值对
* @param headers
* @return
* @throws IOException
*/
public static HttpResponse postFormData(String urlStr, Map<String, String> filePathMap, Map<String, Object> keyValues, Map<String, Object> headers) throws IOException {
HttpResponse response;
HttpURLConnection conn = getHttpURLConnection(urlStr, headers);
//分隔符,可以任意设置,这里设置为 MyBoundary+ 时间戳(尽量复杂点,避免和正文重复)
String boundary = "MyBoundary" + System.currentTimeMillis();
//设置 Content-Type 为 multipart/form-data; boundary=${boundary}
conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
//发送参数数据
try (DataOutputStream out = new DataOutputStream(conn.getOutputStream())) {
//发送普通参数
if (keyValues != null && !keyValues.isEmpty()) {
for (Map.Entry<String, Object> entry : keyValues.entrySet()) {
writeSimpleFormField(boundary, out, entry);
}
}
//发送文件类型参数
if (filePathMap != null && !filePathMap.isEmpty()) {
for (Map.Entry<String, String> filePath : filePathMap.entrySet()) {
writeFile(filePath.getKey(