RestTemplate上传文件解决方案

简介: 当对接文件上传模块时,需要对接上传文件的接口,而我们模块的数据是以字节数组存在的(已经操作过了的字节数组,存在于内存中)接口是以form-data的形式上传的,其中需要上传MultipartFIle,如果使用MultipartFile放入到请求的 fromMap中,然后再上传这个文件,会报(ByteArrayInputStream no serialized)的错误,也就是没有注入对应的bean的错误。。

背景:由于Hutool中的HttpUtil没有对应的连接池,所以使用Spring自带的RestTemplate来进行其他系统的Http信息的对接。

问题出现:当对接文件模组时,需要对接上传文件的接口,而我们模块的数据是以字节数组存在的(已经操作过了的字节数组,存在于内存中)接口是以form-data的形式上传的,其中需要上传MultipartFIle,如果使用MultipartFile放入到请求的 from map中,然后再上传这个文件,会报(ByteArrayInputStream no  serialized)的错误,也就是没有注入对应的bean的错误。

问题代码:

Mapheaders=newHashMap<String, String>(1);
headers.put("Content-Type", "multipart/form-data");
//使用字节数据进行创建文件【方法入参为字节数组】MultipartFilefile=newMockMultiPartFile(filebyte);
MultiValueMap<String, Object>form=newLinkedMultiValueMap<>();
form.add("appId", fileDealConstants.getFileDealAppId());
form.add("file", file);
form.add("filename", fileName);
form.add("fileInvalidDay", "0");


问题报错【截取主要报错】:

org.springframework.http.converter.HttpMessageConversionException: Typedefinitionerror: [simpletype, classjava.io.ByteArrayInputStream]; nestedexceptioniscom.fasterxml.jackson.databind.exc.InvalidDefinitionException: Noserializerfoundforclassjava.io.ByteArrayInputStreamandnopropertiesdiscoveredtocreateBeanSerializer (toavoidexception, disableSerializationFeature.FAIL_ON_EMPTY_BEANS) (throughreferencechain: org.springframework.mock.web.MockMultipartFile["inputStream"])
Causedby: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Noserializerfoundforclassjava.io.ByteArrayInputStreamandnopropertiesdiscoveredtocreateBeanSerializer (toavoidexception, disableSerializationFeature.FAIL_ON_EMPTY_BEANS) (throughreferencechain: org.springframework.mock.web.MockMultipartFile["inputStream"])


百度RestTemplate上传文件的方法,有两种方法解决这种情况。

方法1

使用FileSystemResource进行上传,但是FileSystemResource的创建只能通过 File  或者 文件的路径来生成,也就是说文件已经真实存在磁盘了,所以解决方案是先存到本地然后再进行读取,然后再删除【这种方法看着貌似有点麻烦】,如果需要存到本地的话,还需要判断本地是否有重复的文件,如果删除失败的话,服务器上也会存有许多的垃圾文件,需要定时清除,所以最终采用方法2

此处FileSysteResource的构造函数,只能读取对应的真实存在的文件

publicFileSystemResource(Stringpath)
publicFileSystemResource(Filefile)
publicFileSystemResource(PathfilePath)
publicFileSystemResource(FileSystemfileSystem, Stringpth)


方法2

继承InputStreamResource重写一个构造方法,然后重写getFileName和contentLength即可,可以直接代替MultipartFIle来上传。

参考博客【里面会有一些源码的解析】:https://www.cnblogs.com/shineman-zhang/articles/13070118.html

【写代码过程中的发现】:如果出现InputStream 已经关闭的错误,查看对应的使用方法是不是把InputStream 用完了就关了


CommonInputStreamResource

publicclassCommonInputStreamResourceextendsInputStreamResource {
/**** 文件長度*/privateintlength;
/**** 文件名稱*/privateStringfileName;
publicCommonInputStreamResource(InputStreaminputStream) {
super(inputStream);
    }
publicCommonInputStreamResource(InputStreaminputStream, intlength,StringfileName) {
super(inputStream);
this.length=length;
this.fileName=fileName;
    }
/*** 覆写父类方法* 如果不重写这个方法,并且文件有一定大小,那么服务端会出现异常* {@code The multi-part request contained parameter data (excluding uploaded files) that exceeded}** @return*/@OverridepublicStringgetFilename() {
returnthis.fileName;
    }
/*** 覆写父类 contentLength 方法* 因为 {@link org.springframework.core.io.AbstractResource#contentLength()}方法会重新读取一遍文件,* 而上传文件时,restTemplate 会通过这个方法获取大小。然后当真正需要读取内容的时候,发现已经读完,会报如下错误。* <code>* java.lang.IllegalStateException: InputStream has already been read - do not use InputStreamResource if a stream needs to be read multiple times* at org.springframework.core.io.InputStreamResource.getInputStream(InputStreamResource.java:96)* </code>* <p>* ref:com.amazonaws.services.s3.model.S3ObjectInputStream#available()** @return*/@OverridepubliclongcontentLength() {
intestimate=length;
returnestimate==0?1 : estimate;
    }
}



无问题的代码:

//设置headerMapheaders=newHashMap<String, String>(1);
headers.put("Content-Type", "multipart/form-data");
//根据文件字节数组转对应的 CommonInputStreamResource,然后再上传ByteArrayInputStreaminputStream=newByteArrayInputStream(fileBytes);
CommonInputStreamResourcecommonInputStreamResource=newCommonInputStreamResource(inputStream,fileBytes.length,fileName);
//必须使用LinkedMultiValueMap传参MultiValueMap<String, Object>form=newLinkedMultiValueMap<>();
form.add("appId", fileDealConstants.getFileDealAppId());
//将文件数据放入到map中form.add("file", commonInputStreamResource);
form.add("filename", fileName);
form.add("fileInvalidDay", "0");
StringrequestUrl=fileDealConstants.getFileDealUrl() +fileDealConstants.getUploadPath();
ResponseEntity<String>responseEntity=restTemplateUtil.postResponseEntity(headers, fileDealConstants.getFileDealUrl() +fileDealConstants.getUploadPath(), form, newParameterizedTypeReference<String>(){});


RestTemplateUtil部分代码

@Slf4j@Component@Configuration@AllArgsConstructorpublicclassRestTemplateUtil {
@AutowiredprivateRestTemplaterestTemplate;
public<T>ResponseEntity<T>postResponseEntity(Map<String, String>headers, Stringurl, Objectbody, ParameterizedTypeReference<T>reference, Object... uriVariables) {
MultiValueMap<String, String>map=newHttpHeaders();
if (!ObjectUtils.isEmpty(headers)) {
for (Map.Entry<String, String>entry : headers.entrySet()) {
map.add(entry.getKey(), entry.getValue());
            }
        }
returnrestTemplate.exchange(url, HttpMethod.POST, newHttpEntity<>(body, map), reference, uriVariables);
    }
}



目录
相关文章
|
存储 安全 Java
解析 Java 的 MultipartFile 接口:实现文件上传的全面指南
在现代的 Web 开发中,文件上传是一个常见的需求,而 Java 中的 `MultipartFile` 接口正是用来处理这类任务的重要工具。无论是上传图片、音频、视频还是其他文件类型,`MultipartFile` 都提供了便捷的方法来处理文件的接收和存储。本文将带您深入探索 Java 中的 `MultipartFile` 接口,揭示其功能、用法以及在实际开发中的应用场景。
|
JSON Java 数据格式
|
4月前
|
安全 Java 网络安全
RestTemplate使用文件参数的高级应用案例
将这些高级特性组合起来,可以创建一个 `RestTemplate` 实例,它能够处理各种复杂的请求场景,包括大型文件上传、安全的 https 传输和详细的错误管理,在与外部服务的交互过程中提供强大和灵活的 HTTP 客户端功能。
186 0
|
5月前
|
监控 安全 NoSQL
【SpringBoot】OAuth 2.0 授权码模式 + JWT 令牌自动续签 的终极落地指南,包含 深度技术细节、生产环境配置、安全加固方案 和 全链路监控
【SpringBoot】OAuth 2.0 授权码模式 + JWT 令牌自动续签 的终极落地指南,包含 深度技术细节、生产环境配置、安全加固方案 和 全链路监控
2001 1
|
XML JSON 人工智能
Error while extracting response for type [class xxx] and content type application/xml;charset=UTF-8
Error while extracting response for type [class xxx] and content type application/xml;charset=UTF-8
2332 0
|
11月前
|
XML JSON 前端开发
HTTP协议,Content-Type格式介绍篇
通过理解和正确使用Content-Type头字段,可以确保数据在网络上传输时的正确性和高效性,提升网络应用的可靠性和用户体验。
1148 18
|
XML JSON Java
springboot文件上传,单文件上传和多文件上传,以及数据遍历和回显
本文介绍了在Spring Boot中如何实现文件上传,包括单文件和多文件上传的实现,文件上传的表单页面创建,接收上传文件的Controller层代码编写,以及上传成功后如何在页面上遍历并显示上传的文件。同时,还涉及了`MultipartFile`类的使用和`@RequestPart`注解,以及在`application.properties`中配置文件上传的相关参数。
springboot文件上传,单文件上传和多文件上传,以及数据遍历和回显
|
安全 Java 网络安全
如何在Java中处理SSLHandshakeException异常?
如何在Java中处理SSLHandshakeException异常?
2272 1
|
Java API 开发者
【已解决】Spring Cloud Feign 上传文件,提示:the request was rejected because no multipart boundary was found的问题
【已解决】Spring Cloud Feign 上传文件,提示:the request was rejected because no multipart boundary was found的问题
1817 0
|
Java Linux
POI 生成word 转 pdf
根据业务需要 需要出一份 PDF 文件 作为 公告的附件使用 PDF文件中 需要有 各种数据作为展示 是动态生成的
2967 0
POI  生成word 转 pdf