最近在一次处理上传文件的时候遇到需要guess encode问题,在使用jchardet的过程中使用UniversalDetector
调用handleData(buf, 0, read)
需要传入一个byte buffer,故大致写了如下一个实现试图从MultipartFile获取InputSteam然后读取buffer。
看上去简单明了,不过部署后发现程序似乎进入了infinite loop没有进行正常响应,调试进入发现程序果然陷入第5行持续循环无法结束,经过watcher发现每次loop都只read到了文件开头的字节,并未如预期read完整个Stream,难道是MultipartFile.getInputStream()
实现和预想的不一样?遂step into MultipartFile.getInputStream()
发现调用的是StandardMultipartHttpServletRequest
的实现,StandardMultipartHttpServletRequest
继承自AbstractMultipartHttpServletRequest
,是对当前HttpServletRequest
的封装。这看上去没什么问题,调用的是part
的getInputStream()
实现,没错,这里的part是javax.servlet.http.Part
的JavaEE接口,具体逻辑由容器的实现。
继续跟进,发现part果然进入了org.apache.catalina.core.ApplicationPart
的tomcat实现
这里的fileItem应该是容器对于上传表单中文件的封装,继续跟进fileItem进入了org.apache.tomcat.util.http.fileupload.disk.DiskFileItem
实现,下面是DiskFileItem
的一些关键方法,其中dfos是一个DeferredFileOutputStream
接口。
继续step into进入了org.apache.tomcat.util.http.fileupload.ThresholdingOutputStream
,ThresholdingOutputStream
是DeferredFileOutputStream
的一个实现。果然在上传文件大小大于阈值时候tomcat会将内容放置在一个临时目录里,并不会持续将文件保留在内存中。有趣的是默认的threshold配置是0,换言之就是默认情况下对于上传的文件,只要存在内容,tomcat总会将其持久化,并在调用getInputStream()
方法的时候创建FileInputStream返回。换言之,就是每次调用getInputStream()
返回的都是一个全新的FileInputStream,也解释了为什么在调用getInputStream()
总是只能读取前buffer长度的字节,一直无法读取完全的问题。其中ThresholdingOutputStream
的关键代码如下。