POST发送form-data格式请求:对接第三方系统时的文件传输实战
在进行第三方系统对接的过程中,有时我们需要通过POST请求传递文件,这时就需要用到form-data格式。本文将详细介绍如何构建并发送这种类型的请求,同时展示如何处理多文件集合的传递场景。
发送方(Java实现)
连接配置代码
首先,使用HttpClient构建一个可关闭的HTTP客户端,并设置连接超时、请求超时和Socket超时。同时,初始化MultipartEntityBuilder对象以防止文件名在传输过程中出现乱码问题:
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
RequestConfig requestConfig = RequestConfig.custom()
// 默认连接超时
.setConnectTimeout(6 * 10000)
.setConnectionRequestTimeout(6 * 10000)
// 请求超时
.setSocketTimeout(6 * 10000)
.build();
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setCharset(StandardCharsets.UTF_8);
//设置模式,防止传输附件到第三方系统文件名乱码
multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532);
CloseableHttpResponse httpResponse = null;
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
文件传递代码
为了上传文件,我们使用addBinaryBody方法,并指定ContentType,确保文件名不乱码:
multipartEntityBuilder.addBinaryBody("file", new FileInputStream(file),ContentType.DEFAULT_BINARY,file.getName());
参数传递代码
所有参数推荐使用StringBody进行封装,并明确指定字符集类型,以防止中文乱码:
Iterator<String> iterator = params.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Object value = params.get(key);
StringBody stringBody = new StringBody(objIsEmpty(value), ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8));
multipartEntityBuilder.addPart(key, stringBody);
}
请求执行与响应处理代码
创建完整的HttpEntity实体,将其添加至HttpPost请求中,执行请求并获取响应内容:
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
httpResponse = httpClient.execute(httpPost);
HttpEntity responseEntity = httpResponse.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
StringBuffer buffer = new StringBuffer();
String str;
while (!StringUtils.isEmpty(str = reader.readLine())) {
buffer.append(str);
}
httpClient.close();
if (httpResponse != null) {
httpResponse.close();
}
完整工具类示例
这里提供了一个自定义的HttpUtil工具类,包含单文件和多文件上传的方法:
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.druid.util.StringUtils;
import com.alibaba.fastjson.JSON;
import org.apache.commons.collections.MapUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HTTP;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class HttpUtil {
private final static ContentType CONTENT_TYPE = ContentType.create(HTTP.PLAIN_TEXT_TYPE, HTTP.UTF_8);
private HttpUtil() {
}
public static String postFormData(String url, Map<String, Object> params, File file) throws IOException {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
RequestConfig requestConfig = RequestConfig.custom()
// 默认连接超时
.setConnectTimeout(6 * 10000)
.setConnectionRequestTimeout(6 * 10000)
// 请求超时
.setSocketTimeout(6 * 10000)
.build();
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setCharset(StandardCharsets.UTF_8);
//设置模式,防止传输附件到第三方系统文件名乱码
multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532);
CloseableHttpResponse httpResponse = null;
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
if (file == null) {
throw new RuntimeException("文件不能为空");
}
multipartEntityBuilder.addBinaryBody("file", new FileInputStream(file),ContentType.DEFAULT_BINARY,file.getName());
if (MapUtils.isNotEmpty(params)) {
Iterator<String> iterator = params.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Object value = params.get(key);
StringBody stringBody = new StringBody(objIsEmpty(value), CONTENT_TYPE);
multipartEntityBuilder.addPart(key, stringBody);
}
}
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
httpResponse = httpClient.execute(httpPost);
HttpEntity responseEntity = httpResponse.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
StringBuffer buffer = new StringBuffer();
String str;
while (!StringUtils.isEmpty(str = reader.readLine())) {
buffer.append(str);
}
httpClient.close();
if (httpResponse != null) {
httpResponse.close();
}
return buffer.toString();
}
public static String postFormData(String url, Map<String, Object> params, List<File> files) throws IOException {
CloseableHttpClient httpClient = HttpClientBuilder.create().build();
RequestConfig requestConfig = RequestConfig.custom()
// 默认连接超时
.setConnectTimeout(6 * 10000)
.setConnectionRequestTimeout(6 * 10000)
// 请求超时
.setSocketTimeout(6 * 10000)
.build();
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
multipartEntityBuilder.setCharset(StandardCharsets.UTF_8);
//设置模式,防止传输附件到第三方系统文件名乱码
multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532);
CloseableHttpResponse httpResponse = null;
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
if (CollectionUtil.isEmpty(files)) {
throw new RuntimeException("文件不能为空");
}
for (File file : files) {
multipartEntityBuilder.addBinaryBody("files", new FileInputStream(file),ContentType.DEFAULT_BINARY,file.getName());
}
if (MapUtils.isNotEmpty(params)) {
Iterator<String> iterator = params.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
Object value = params.get(key);
StringBody stringBody = new StringBody(objIsEmpty(value), CONTENT_TYPE);
multipartEntityBuilder.addPart(key, stringBody );
}
}
HttpEntity httpEntity = multipartEntityBuilder.build();
httpPost.setEntity(httpEntity);
httpResponse = httpClient.execute(httpPost);
HttpEntity responseEntity = httpResponse.getEntity();
BufferedReader reader = new BufferedReader(new InputStreamReader(responseEntity.getContent()));
StringBuffer buffer = new StringBuffer();
String str;
while (!StringUtils.isEmpty(str = reader.readLine())) {
buffer.append(str);
}
httpClient.close();
if (httpResponse != null) {
httpResponse.close();
}
return buffer.toString();
}
}
接收方(Spring MVC控制器接收)
单文件接收代码
在Spring MVC控制器中,可以通过MultipartFile参数来接收单个文件:
@PostMapping("***")
public ResultDTO insertTacticFlow(TacticFlowVo tacticFlowVo, MultipartFile file) {
return ResultDTO.successData(service.insertTacticFlow(tacticFlowVo, file));
}
多文件接收代码
对于多个文件的接收,可以通过List类型的参数进行接收:
@PostMapping("***")
public ResultDTO acceptOperationFlowEnd(HttpServletRequest request, List<MultipartFile> files) {
// 获取请求中的Map数据
Map<String, String[]> paramMap = request.getParameterMap();
service.acceptOperationFlowEnd(paramMap,files);
return ResultDTO.success();
}
接收方注意事项
重要提示: 在处理form-data格式的POST请求时,注意不能直接使用@RequestBody配合Map接收非文件字段的参数数据。这是因为form-data数据不会被自动映射到Map或对象上。正确的做法是使用对象接收或者从HttpServletRequest中手动获取其他非文件字段的参数。