优雅的把MultipartFile转换成File


网上的很多MultipartFile转File,都是只说了方法而没有分析,甚至有一些是不可用的方法。所以我根据自己的开发经验对FileUpLoad的这个小妖精类进行了一些总结,以飨读者。

FileUpLoad在Spring的配置

先说说配置。大家在配置Spring文件上传的时候想必都知道这个配置:

Spring的FileUpLoad有一个参数,设置文件流存在内存里的最大值。如果没有超过这个值,那么从前台传入的文件会直接存在内存里,而不写入IO,这种措施可以有效提高文件存取效率,但是也带来一些问题。

有时候后台作为一个调用第三方api的中介者,需要使用Java调用第三方接口,会有使用InputStream类。而InputStream类只有传入File类的构造方法,所以需要把Multipart类转换成File类。而Multipart类是一个独立的接口,不支持强转成File类型,那我们需要怎么做呢?

用一个内置的API

首先,Multipart类提供了一个transferTo(File file)方法可以把Multipart中的文件流塞进File类。可以解决转换的问题,看似很完美,但是带来了一些新的问题:

例如,创建file类需要一个path,调用这个方法会在服务器某个角落创建出一个文件,如果吞吐量很大,频繁的IO会拖慢效率,存储在服务器的大量文件也是一个问题。

而且如果path不存在,还会直接抛出异常,不利于业务逻辑处理。

所以这种情况只适合需要存储文件的情况。

强转成接口实现类

第二种就是直接强转成接口实现类:

CommonsMultipartFile和DiskFileItem都是接口实现类,通过调用该类的方法就可以获取Multipart里面的文件流。一开始我以为这样做是不会存成一个临时文件的。只是后来发现使用这种方法,在post第三方接口时偶尔会抛出一个无法找到该文件的异常,类似这样:

/usr/local/apache-tomcat-7.0.73/work/Catalina/localhost/anti-fraud/upload_575d8541_4e25_43d4_b173_8d800487df74_00000043.tmp (No such file or directory)

一开始很好奇为什么会出现这样的错误,经过多方查找,结果发现是文首提到的配置MaxInMemeroy参数问题。FileUpLoad会将一些小文件存在内存里,然后通过判断isInMemory判断具体的接口实现类。所以如果配置太大,则会使文件不存成缓存文件,导致在获取File时不能通过路径获取,导致在写入请求流时获取到null文件,接口抛出异常。

自己想到的

这个方法最大优势在于,不论多么复杂的文件夹嵌套以及文件名,不存在情况下,工具类都会帮你成功创建,而不是直接跑出一个异常。

一些想法

由于File类和文件路径千丝万缕的联系,所以我们无法直接通过一个流来获得一个文件,只能缓存在本地然后继续对文件进行发送处理。(如果只是分析一个文件的信息,那么流也可以做到)。所以直接请求是做不到了,我们只能通过具体的业务来具体使用哪一种方法。

首先个人不推荐transferTo方法,第一是我自己不喜欢try语句,第二是这种方法很依赖于项目的目录,如果没有这个目录就会存储失败,抛出异常,所以对于需要存储图片的情况,个人推荐还是用FileUtil,这种方法可以保证图片存储成功。至于CommonsMultipartFile那个方法,如果觉得这些图片不需要保存,或者想要看似透明的转换一个文件而不留痕迹,那就推荐使用这种方法。不过我个人在项目中使用这种方法总是会遇到一些奇奇怪怪的问题。 有些人测试接口总是特别狗的会用一个空文件测试,导致抛出异常。而且这种一不小心就会中断的业务逻辑实在不适合存在于一个上线的项目中。所以权衡之下,FileUtil类这种能良好处理业务逻辑的方法可能会是最优的选择。但是最好还是应该专门规定一个目录存储图片作为log,然后对流进行转换。

对MultipartFile的一些研究

MultipartFile的实现类中有一个FileItem的参数,这个FileItem存储了上传过来的文件的流,这些流有些是在内存里,有些在传过来时就已经被fileUpLoad处理成临时文件。高性能的后台都会尽量的优化IO,fileUpLoad缓存在内存里的原因之一也是如此。那么如果要对图片的分辨率或者是其他信息进行检测该怎么办呢?

一般来说,我们都会像上文一样,先将图片保存在服务器的硬盘上,然后读取这个图片的信息,如果不符合则删除,符合再继续执行其他操作。这样的多IO显然违背了高性能开发,也没有合理的利用内存这个机制,其实只要使用流就可以获取图片的信息。

在MultipartFile里面有个方法是getInputStream(),ImageIo有个方法是read()参数类型正好是输入流值是一个BufferedImaged。所以我们可以类似这样

然后再判断这个img的相关属性,如果对的话再将其写入硬盘中,如果不符合就丢弃。

写在最后

其实MultipartFile还是有挺多值得研究的。不过因为这个东西更多的在于实际应用,而且对这类应用框架的原理的研究不如对架构框架的收益多。所以网上的资料实在是难找。我自己找过挺多这方面的资料,在此并加上一些自己的想法和研究总结成文,发出来,算是给大家一个参考。

声明:夜语|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 优雅的把MultipartFile转换成File


我不和你谈事,只想和你谈心。🍂