Resttemplate上传文件500异常的原因及解决方法
一、问题背景
使用 Resttemplate 调用 DMS 文件服务器 Http 接口,出现 500 异常报错
二、问题描述
通过 ClientHttpRequestInterceptor 拦截器设置 ContentType 和 BasicAuth 后,调用DMS 文件服务器 Http 接口,出现 500 异常报错
三、相关代码
Resttemplate 及自定义 ClientHttpRequestInterceptor Bean 对象代码
@Bean(name = "dmsRestTemplate")
public RestTemplate restTemplate(
@Qualifier("dmsRequestInterceptor") ClientHttpRequestInterceptor dmsRequestInterceptor) {
RestTemplate restTemplate = new RestTemplate();
((AllEncompassingFormHttpMessageConverter) restTemplate.getMessageConverters().get(4))
.setMultipartCharset(StandardCharsets.UTF_8);
restTemplate.getInterceptors().add(dmsRequestInterceptor);
restTemplate.setErrorHandler(new DefaultResponseErrorHandler() {
@Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return super.hasError(response.getStatusCode())
&& !HttpStatus.NOT_FOUND.equals(response.getStatusCode());
}
@Override
public void handleError(ClientHttpResponse response) throws IOException {
super.handleError(response);
}
});
return restTemplate;
}
@Bean
public ClientHttpRequestInterceptor dmsRequestInterceptor(FileStorageProperties fileStorageProperties) {
String userName = fileStorageProperties.getDms().getUserName();
String password = fileStorageProperties.getDms().getPassword();
return (request, body, execution) -> {
HttpHeaders headers = request.getHeaders();
MediaType type = MediaType.parseMediaType(MediaType.MULTIPART_FORM_DATA_VALUE);
headers.setContentType(type);
headers.setBasicAuth(userName, password, StandardCharsets.UTF_8);
return execution.execute(request, body);
};
}
文件上传代码
@Override
public FileStorageBO upload(String filePath) throws BaseAppException {
LOGGER.debug("DMSFileStorage upload by file path start.");
if (StringUtils.isEmpty(fileStorageProperties.getDms().getRestUrl())) {
LOGGER.error("DMS rest url config not exist!");
ExceptionHandler.publish("EPA-COMMON-0010", ExceptionHandler.BUSS_ERROR);
}
File tempFile = new File(filePath);
FileStorageBO result = new FileStorageBO(tempFile);
MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
FileSystemResource resource = new FileSystemResource(tempFile.getAbsolutePath());
postParameters.add("file", resource);
postParameters.add("shardingkey", "EPA_" + getShardingKey());
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(postParameters);
try {
String dmsId = dmsRestTemplate.postForObject(fileStorageProperties.getDms().getRestUrl() + "/file",
httpEntity, String.class);
result.setFileIdentity(dmsId);
LOGGER.debug("DMSFileStorage upload by file path end. dms id is {}", dmsId);
}
catch (RestClientException e) {
LOGGER.error(e);
ExceptionHandler.publish("EPA-COMMON-0011", ExceptionHandler.BUSS_ERROR, e.getLocalizedMessage());
}
return result;
}
四、问题原因
拦截器中设置 ContentType 为 multipart/form-data 时,无法自动生成 boundary 信息,导致无法根据该分隔符信息来解析请求体
在使用multipart/form-data格式发送HTTP请求时,请求体的数据会被分割成多个部分,每个部分之间使用一个特定的分隔符(boundary)进行分隔。这个boundary是在Content-Type头部中指定的,用于告诉服务器如何识别每个部分的开始和结束。
当使用RestTemplate发送multipart/form-data请求时,RestTemplate会自动处理boundary的生成和添加到请求的Content-Type头部中。这样服务器就能够正确解析请求体中的各个部分。
使用RestTemplate构建multipart/form-data请求时,只需设置正确的Content-Type为multipart/form-data,RestTemplate会负责生成请求体的各个部分,并在Content-Type头部中添加正确的boundary信息。
不需要手动追加boundary到请求体中,RestTemplate会在内部处理这一细节,确保请求能够正确地被服务器解析。
因此,使用RestTemplate发送multipart/form-data请求时,只需设置正确的Content-Type为multipart/form-data,RestTemplate会负责处理boundary的生成和添加,确保请求能够正确发送到服务器并被正确解析。
通常情况下,Content-Type的设置应该由发送请求的代码负责,而不是在ClientHttpRequestInterceptor中进行设置。ClientHttpRequestInterceptor主要用于在发送请求之前或之后对请求进行修改或处理,例如添加认证信息、日志记录、错误处理等。
五、问题修复
将 ContentType 移到上传代码中
拦截器 Bean:
@Bean
public ClientHttpRequestInterceptor dmsRequestInterceptor(FileStorageProperties fileStorageProperties) {
String userName = fileStorageProperties.getDms().getUserName();
String password = fileStorageProperties.getDms().getPassword();
return (request, body, execution) -> {
HttpHeaders headers = request.getHeaders();
headers.setBasicAuth(userName, password, StandardCharsets.UTF_8);
return execution.execute(request, body);
};
}
上传代码:
@Override
public FileStorageBO upload(String filePath) throws BaseAppException {
LOGGER.debug("DMSFileStorage upload by file path start.");
if (StringUtils.isEmpty(fileStorageProperties.getDms().getRestUrl())) {
LOGGER.error("DMS rest url config not exist!");
ExceptionHandler.publish("EPA-COMMON-0010", ExceptionHandler.BUSS_ERROR);
}
File tempFile = new File(filePath);
FileStorageBO result = new FileStorageBO(tempFile);
MultiValueMap<String, Object> postParameters = new LinkedMultiValueMap<>();
FileSystemResource resource = new FileSystemResource(tempFile.getAbsolutePath());
postParameters.add("file", resource);
postParameters.add("shardingkey", "EPA_" + getShardingKey());
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType(MediaType.MULTIPART_FORM_DATA_VALUE);
headers.setContentType(type);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(postParameters, headers);
try {
String dmsId = dmsRestTemplate.postForObject(fileStorageProperties.getDms().getRestUrl() + "/file",
httpEntity, String.class);
result.setFileIdentity(dmsId);
LOGGER.debug("DMSFileStorage upload by file path end. dms id is {}", dmsId);
}
catch (RestClientException e) {
LOGGER.error(e);
ExceptionHandler.publish("EPA-COMMON-0011", ExceptionHandler.BUSS_ERROR, e.getLocalizedMessage());
}
return result;
}
以上就是Resttemplate上传文件500异常的原因及解决方法的详细内容,更多关于Resttemplate上传文件500的资料请关注脚本之家其它相关文章!
相关文章
SpringBoot基于FFmpeg实现压缩视频切片为m3u8
本文介绍了一个使用FFmpeg将MP4视频压缩切片为HLS格式M3U8文件的Java工具类,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下2026-02-02
springboot配置文件属性变量引用方式${}和@@用法及区别说明
这篇文章主要介绍了springboot配置文件属性变量引用方式${}和@@用法及区别说明,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-03-03
我们来说说Java LockSupport 的 park 和 unpa
LockSupport是JDK底层线程阻塞工具,通过park/unpark实现线程阻塞与唤醒,避免死锁,与Object的wait/notify相比,无需监视器,支持先unpark后park,且更精确控制线程状态,是AQS等同步器的核心基础,本文介绍Java LockSupport 的 park 和 unpark,感兴趣的朋友一起看看吧2025-07-07


最新评论