文件上传流从哪里获取内容?

我有一个关于文件上传的问题,这与它的工作原理有关,而不是代码问题。 我在互联网上看了,但我找不到合适的答案。

我有一个在tomcat上运行的Web应用程序,它处理文件上传(通过servlet)。 假设我现在要上传大文件(> 1 Gb)。 我的理解是,一旦实际传输了整个文件,HTTP请求的多部分内容就可以在我的servlet中使用。

我的问题是请求的内容实际存储在哪里? 当一个人调用HttpServletRequest.getParts()时, Part对象上有一个InputStream 。 但是,从哪里读取流? Tomcat将它存储在某个地方吗?

我想这可能不够清楚,所以我会根据你的意见更新post,如果有的话。

谢谢

Tomcat将Part存储在“X:\ some \ path \ Tomcat 7.0 \ temp”(/some/path/apache-tomcat-7.0.x/temp)目录中。

解析多部分请求时,如果单个部分的大小超过阈值,则为该部分创建临时文件。

完成所有部件的传输后,将调用您的servlet / jsp。

当请求被销毁时,所有临时文件也会被删除。

如果您对多部分解析阶段感兴趣,请查看apache commons-fileupload(特别是ServletFileUpload.parseRequest() ),tomcat基于该变体

UPDATE

你可以将它配置为java arg,即在windows中:

在此处输入图像描述

InputStream通常会从请求期间由multipart框架创建的临时文件中读取。 临时文件通常存储在应用程序服务器的临时区域中 – 由servlet上下文属性javax.servlet.context.tempdir指定。 在Tomcat中,这是$CATALINA_HOME/work下面的某个地方。 请求完成后,将删除该文件。

对于小文件大小,multipart框架可以将整个上传保留在内存中 – 在这种情况下, InputStream将直接从内存中读取。

如果您使用的是Spring的CommonsMultipartResolver则可以通过maxInMemorySize属性设置内存中允许的最大上载大小。 如果上传大于此值,则它将作为临时文件存储在磁盘上。

我想我们应该退一步,对网络基础设施进行思考。 首先,HTTP传输文本数据,因此在基础64中编码的二进制信息使得数据不会被弄乱。 这最终会导致大量的数据,这就产生了多部分forms,它将数据分成编码文本的一部分,并带有特殊标记,允许服务器将所有内容组合在一起。 但要使用这些数据,我们必须先解码它,为此,我必须使用表单的多个部分。

[rest让我们可以呼吸]

继续,所以浏览器需要发送大量的数据(如你在例子中提到的1GB),这个数据用base64编码,然后用它的标记分成片段(多部分forms),然后浏览器开始发送片段到服务器,但服务器只有在完成接收处理HTTP REQUEST后才返回HTTP RESPONSE(或者如果发生超时,则会在浏览器屏幕上出现错误)。

这里可以假设 Tomcat可以(我没有检查内部)开始解码已经存在的多部分的每个部分(从临时文件或从内存中)将输入流传递给用户,因为输入的读取是一个阻塞操作,服务器将等待下一个数据传递给Tomcat,然后Tomcat将它传递给处理数据的程序。

一旦所有数据到达服务器,程序将准备Tomcat将返回到完成HTTP请求 – 响应周期关闭连接的浏览器的响应(因为HTTP是无连接协议)。

希望能帮助到你 :)

Tomcat遵循Servlet 3.0规范,允许您指定诸如多部分“部分”在磁盘上存储(临时)之前的大小,临时文件将写入的位置,文件的最大大小是多少,以及整个请求的最大大小。 您可以在此处和此处找到有关配置分段上传(在Tomcat或任何其他符合规范3.0的服务器中)的各种有用信息。

Tomcat的实现细节并不十分相关:它遵守规范。 如果要上载的文件小于设置的阈值,那么您应该能够从内存中读取文件的字节(即不涉及磁盘)。 如果文件较大,则首先(完整地)将其写入磁盘,然后您可以从容器中获取字节。

因此,如果你想收到一个1GiB文件,并且没有那种可用的内存(我不建议允许客户端为每次上传添加1GiB的上传数据填充你的…如果你刚开始就轻松做DoS几个同时上传1GiB并且你是吐司),然后Tomcat(或你正在使用的任何容器)将文件(再次,完整地)读取到磁盘上,当你的servlet获得控制时,你可以从那里读取字节文件。

请注意,在您的任何代码真正运行之前,容器必须处理整个多部分请求。 这是为了防止你通过部分读取请求的InputStream或类似的东西来破坏任何东西。 处理多部分请求并非易事,而且很容易破解。

如果您希望能够流式处理大型文件以进行处理(例如,可以串行处理的大型XML文件),那么您将希望自己处理多部分解析。 这样,您不需要大量的堆来缓冲文件,并且在开始处理之前不需要将文件存储在磁盘上。 (如果这是您的用例,我建议使用HTTP PUT或HTTP POST,而不是使用多部分请求。)

(值得一提的是,在任何多部分处理规范中都没有提到base64编码。有些人在这里提到过base64,但我从未见过标准的Web客户端使用base64来使用multipart / form-data上传文件。处理二进制上传就好了,谢谢。)

就这个

  1. 用户的浏览器组成http多部分请求
  2. 用户操作系统的Tcp / ip堆栈将它们分成数据包
  3. 通过Internet的路由器将这些数据包传递给您的服务器
  4. 服务器操作系统的Tcp / ip堆栈返回有效负载并将它们传递给tcp端口侦听器
  5. Tomcat http连接器解码来自tcp数据的http post请求(源代码为https://github.com/apache/tomcat/tree/trunk/java/org/apache/coyote )
  6. Tomcat http连接器包装一个Http请求并最终转发到您的servlet( https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/connector/Request.java
  7. 在您的代码读取Http Request的内容之前和之后,tomcat将在内部缓冲http请求体
  8. 在调用request.getParts()( https://github.com/apache/tomcat/blob/trunk/java/org/apache/catalina/connector/Request.java#L2561 )之前,Tomcat不会解析多个部件主体调用前没有部件的临时文件。
  9. Tomcat将上传到位置的文件存储在servlet代码中的@MultipartConfig注释中,除非你的代码没有提供它并且设置了allowCasualMultipartParsing( http://tomcat.apache.org/tomcat-7.0-doc/config/context.html #Common_Attributes )
  10. 默认情况下,考虑allowCasualMultipartParsing为false,您不必担心tomcat存储文件的位置,尽管很容易挖掘出来。

我提到1~5因为通过request.getInputStream()理解流返回是很重要的,这是Servlet 3.x request.getParts()function之前所必需的。 通常,tomcat会很快将请求发送到Web应用程序,没有必要等待客户端完成上传,因此tomcat不需要缓冲大量数据。 在JSR-000315被批准之前,我已离开java服务器端多年:-)