导航菜单
路很长,又很短
博主信息
昵   称:Cocodroid ->关于我
Q     Q:2531075716
博文数:346
阅读量:1546520
访问量:182859
至今:
×
云标签 标签球>>
云标签 - Su的技术博客
博文->>首页 博主的更多博文>>
记一次文件上传的诡异问题:Jackson多版本之坑
Tags : Jackson,文件上传发表时间: 2021-08-09 16:08:52
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。
比如: 转自:Su的技术博客  原文地址:
一、背景

最近帮一个同事看他的问题,在文件上传功能(包含其他业务逻辑,这里不重要就不指出了)出现比较诡异的问题。前两天都是正常可以使用,文件上传失败,又出现未知异常!而且了解最近都没改过这功能,通过git log查看确实没动过,所以就很奇怪了。

接下来看下文件上传的交互:

ng-web前端层,controller中台层,微服务业务层。

简单来说就是通过前端选择文件之后进行表单提交,后端使用MultipartFile接收文件,接着再转换成Base64使用JSON调用微服务,微服务接收到Base64再转化成byte数组数据,最后将数据存放到云存储OSS上,紧接着进行一系列的业务逻辑操作。

其实这里最好的架构就是:在controller这层进行文件上传,将文件存到云存储,然后返回文件的URL地址。因为文件一般也会比较大,在rpc等交互场景下可能存在问题:多次传输浪费网络带宽,带宽占用大,GC频繁、OOM等。


二、问题排查

2.1、业务层:微服务模块 由于Controller这层不会做其它逻辑,基本上将前端的数据进行透传到后端微服务,所以先看下微服务相关的异常日志:

javax.ws.rs.NotSupportedException: Cannot consume content type

由此异常我们得知,在进行请求时没有设置正常的Content-Type,如:application/json。

但是,通过源码,检查都是发现有调用,本地使用PostMan工具进行模拟都是正常的,那么到底是什么原因?

2.2、中台层:Controller模块

因为我们Controller后端用了统一异常处理器,如果出现未知异常,我们这边都会统一提示:系统异常。所以此时就要查看Controller这层到底是出现什么异常,查看其异常日志如下:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: Param["xxxFile"]->org.springframework.web.multipart.commons.CommonsMultipartFile["fileItem"]->org.apache.commons.fileupload.disk.DiskFileItem["inputStream"])

......

java.io.IOException: HttpResponseProxy{HTTP/1.1 415 Unsupported Media Type

......

由此异常可知,此时是由于json序列化对象时出现异常,即序列化对象包含了MultipartFile字段,序列化时没有对应的serializer进行序列化所爆出的异常。

其实到这里我们大概知道了什么原因,但是有个问题?为什么在Controller抛出异常了,为啥还会请求到微服务呢?这不太正常!
那么现在就从controller层源码进行一探究竟!
2.3、水落石出
1)忽略MultipartFile字段

那么,使用@JsonIgnore注解是可以在Jackson进行序列化时忽略其字段,那么应该是可以这样解决的。

        @JsonIgnore

        private MultipartFile xxxtFile;   

重新进行打包,还是出现一样的异常,这就很疑惑了....
2)不传递xxxFile字段值
为了不影响测试进度,暂时也没有及时发现问题,只能先用这种方式处理,即,将setXxxFile(null)。虽然这样可以解决但是,还没有找到最根本的问题。
3)罪魁祸首——Jackson版本冲突
发现项目里竟然使用了Jackson的两个版本1.X和2.X。
然鹅两个版本差距还是很大的:
1.X 版本包名是codehaus,只提供bug-fix,
2.X 版本包名是fasterxml,还在不断开发和发布中。
所以,当前还是推荐使用2.X版本,即fasterxml Jackson。
所以使用了不同版本,此时就会引入不同版本的类,就会造成注解不起作用。当前使用fasterxml @JsonIgnore注解就可以解决此问题。
2.4、问题收尾
为什么在Controller抛出异常了,为啥还会请求到微服务呢?
要搞清楚这个问题,还是得从源码着手。
由此可知,对象转化成json字符串时进行了捕获异常,并将其异常进行打印,出现异常之后返回null。

往后的源码可以看出json字符串不为空,则将其content-Type设置为application/json,这也可以看出为什么微服务那边会抛出这个异常(NotSupportedException: Cannot consume content type)的原因所在了。


三、总结
1)文件等对象上传不要跨层,最好就是在入口上处理完成之后将其资源URL往后传。
2)使用Jackson序列化工具时注意版本1.X和2.X,最好不要混用,推荐使用2.X fasterxml版本。

3)工具的封装要注意异常的处理,该捕获则捕获,该抛出则抛出,不要让使用者疑惑。


参考资料:
1、Jackson fasterxml和codehaus的区别
https://www.cnblogs.com/leilong/p/8971385.html
打赏
打赏
关注公众号
公众号
类别:Java| 阅读(239)| 赞 (1)
上一篇 : iOS请求访问文件网关服务图片接口异常问题的解决
下一篇: 升级需谨慎,开发两行泪!——记一次MySQL驱动包升级引
评论
暂无评论!
发表评论
昵  称:

验证码:

内  容:

    同时赞一个 赞