Resttemplate上传文件500异常的原因及解决方法
作者:NullInfo
一、问题背景
使用 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的资料请关注脚本之家其它相关文章!