【踩坑记录】springboot用实体类接收MultipartFile,前后端请求问题
1、后端将MultipartFile包在实体类中,框架接收到参数,在有值的情况下,是可以成功找到参数解析器进行赋值的。如果前端表单中的文件选择框不选择资源进行提交,则是空串或"null",后端框架无法正常赋值。在GenericConversionService这个类中有这么一段代码=null);}在前端请求数据中file为空值或"null"值时,后端在参数解析的时候类型无法匹配,则会导致报错。我在
起因
后端
功能需求,需要提交文件加业务属性数据,所以后端就写了实体类用作接口参数,实体类中包含了多个MultipartFile类型及其他数据字段,接口为请求类型为post,未加其他注解参数。
爬坑过程
第一次
前端写了个表单提交,在表单中所有文件选择框都选择文件时,可以正常提交,如果表单中有文件框没有选择,则后端就会报错。此时我给出了第一个建议,建议改为ajax选择性提交,因为默认的表单提交,在后端用实体类接收时,会有一个参数赋值过程,MultipartFile如果为空值,赋值就会报错。改成ajax之后,文件选择框都不选择文件的时候,可以调用成功,但是选择文件后,前端会报错‘Failed to execute ‘arrayBuffer’ on ‘Blob’: Illegal invocation’。
第二次
翻阅关于"Failed to execute ‘arrayBuffer’ on ‘Blob’: Illegal invocation"错误的解决,有把文件类型从实体类拿出来放在请求参数中,加上@RequestPart注解来取值的,也有在*Mapping的注解中加headers="content-type=multipart/form-data"参数的,都试过,依然不行。
第三次
把后端接口恢复到最初,大致如下
@PutMapping(value = "/demo")
public xxx demo(XXDTO xxDTO){
return xxx;
}
前端ajax增加如下属性
contentType: false,
processData: false,
cache: false,
ajax全代码示例如下
var id = $("#id").val();
var name= $("#name").val();
var file1 = $("#file1").prop('files');
var file2 = $("#file2").prop('files');
var formData = new FormData();
formData.append("id", id);
formData.append("name", name);
if(file1!=null && file1!='' && file1!=undefined && file1.length != 0){
formData.append("file1", file1[0]);
}
if(file2!=null && file2!='' && file2!=undefined && file2.length != 0){
formData.append("file2", file2[0]);
}
$.ajax({
type: "put",
url: "/demo",
data: formData,
dataType: "json",
contentType: false,
processData: false,
cache: false,
success: function(data){
// 成功
},
error:function(data){
// 失败
}
});
如上修改之后,接口调用全部正常,至此问题解决。
总结
1、后端将MultipartFile包在实体类中,框架接收到参数,在有值的情况下,是可以成功找到参数解析器进行赋值的。如果前端表单中的文件选择框不选择资源进行提交,则是空串或"null",后端框架无法正常赋值。
在GenericConversionService这个类中有这么一段代码
@Override
public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) {
Assert.notNull(targetType, "Target type to convert to cannot be null");
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
在前端请求数据中file为空值或"null"值时,后端在参数解析的时候类型无法匹配,则会导致报错。我在调试过程中,跟踪到这里时,
targetType值为@io.swagger.annotations.ApiModelProperty org.springframework.web.multipart.MultipartFile
sourceType值为java.lang.String
明显类型不匹配,在对象参数赋值的时候,肯定是会报错的。如果传值时是空值则不提交这个参数,后端在解析时默认是不会解析非必传参数的。
其中更多细节代码感兴趣的同学可以自行学习。
2、前端ajax请求属性设置
官方解释
contentType 发送数据到服务器时所使用的内容类型。默认是:"application/x-www-form-urlencoded"。
processData 布尔值,规定通过请求发送的数据是否转换为查询字符串。默认是 true。
cache 布尔值,表示浏览器是否缓存被请求页面。默认是 true。
主要是contentType 和 processData
processData 默认为true,也就是会将请求参数转换为contentType匹配的格式进行请求,而contentType默认为"application/x-www-form-urlencoded",这个时候加上文件上传,格式化后请求参数中的文件数据则会错乱,导致无效。但是将contentType设置为"multipart/form-data"的话,常规数据又会无效。通过这个意思去理解,那么想要得效果就是希望ajax请求的时候不要去转换请求数据,请求的时候是什么就传什么,交给后端去解析请求参数就可以了。因此将processData设置为false,不让它去转换发送数据,将contentType设置为false,不指定内容类型。
如有错误和不足之处,希望各位同行可以纠正和补充,避免误导。
更多推荐
所有评论(0)