用JAVA实现大文件上传及显示进度信息

  • 作者: 凯哥Java(公众号:凯哥Java)
  • 工作小总结
  • 时间:2022-02-19 03:41
  • 2665人已阅读
简介 一. 大文件上传基础描述:  各种WEB框架中,对于浏览器上传文件的请求,都有自己的处理对象负责对Http MultiPart协议内容进行解析,并供开发人员调用请求的表单内容。比如:Spring 框架中使用类似CommonsMultipartFile对象处理表二进制文件信息。而.NET 中使用HtmlInputFile/ HttpPostedFil

🔔🔔好消息!好消息!🔔🔔

 如果您需要注册ChatGPT,想要升级ChatGPT4。凯哥可以代注册ChatGPT账号代升级ChatGPT4

有需要的朋友👉:微信号 kaigejava2022

大文件上传基础描述:

  各种WEB框架中,对于浏览器上传文件的请求,都有自己的处理对象负责对Http MultiPart协议内容进行解析,并供开发人员调用请求的表单内容。

比如:

Spring 框架中使用类似CommonsMultipartFile对象处理表二进制文件信息。

.NET 中使用HtmlInputFile/ HttpPostedFile对象处理二进制文件信息。

优点:使用框架内置对象可以很方便的处理来自浏览器的MultiPart二进制信息请求,协议分析操作不用开发人员参与。

缺点:其接收数据包过程完全被封闭在框架内置对象中,直到本次请求信息处理(接收)完毕后,才允许开发人员从接口调取表单及文件内容。上传过程中的进度信息无法访问,无法上传大尺寸文件(比如几百兆以上的大文件二进制信息)。

目标:我们要在JAVA WEB框架中,依靠Filter过滤器的能力,实现不依靠框架内置对象,从浏览器请求字节流中解析MultiPart协议,取得本次用户请求的所有信息,包括多二进制文件信息及其他表单项信息。用户上传的文件尺寸将不受限制。而且在传输过程中,我们可以实时获得当前传输进度信息。

 

注:.NET框架中可依靠IHttpModule接口对象达到JAVA框架中Filter的能力,本文不做描述。

 

本文最终完成图:

5981d9be58c3b5ccc4c16c2def6c0a70.png

1.1 普通Post请求协议及MultiPart协议

普通POST请求协议,见图:

462e8c949f95b254ffc333744a343255.png

Content-Length为请求信息内容的字节长度

最下方红圈内为本次表单请求信息

MultiPart请求协议,见图:

7e8eab95f767c0fbae5946239c36edd5.png

Content-Length 为本次请求的内容长度字节,本例729366

Content-Type multipart/form-data,二进制多段表单

Boundary为多段表单信息的分隔符,这里为-----------------------------------7dflaxxxxxxxxxxx

最后一段信息中,name="file1",为本文件表单的单元名称,filename="untitled2.png"为该文件名,content-type: image/png为内容区文件格式

最下方的红框中为该文件的二进制信息。

 

由以上两图可见,MultiPart与普通的POST在协议结构上有明显区别,所以我们接下来的工作就是按字节流的方式接收MultiPart请求数据包,并对其进行分析。

 

1.2 可实时获取当前传输进度信息

  由于我们可以从上述的Http头中获取本次请求内容区长度,即字节总量。由于我们可以从Filter中按字节单位接收来自浏览器的数据包,所以我们也能实时的获得当前接收字节量。因此我们可以实时的获得当前传输进度百分比,用当前接收的字节量除以接收时间即可获得当前传输率(字节/)

  由此,我们可获得以下传输过程信息:

  • 本次数据包总字节数

  • 当前已接收的字节数

  • 本次请求发起时间

  • 当前进度节点时间

  • 当前进度状态(初始状态,接收数据中,接收数据完毕等)

2c0654732bb616d1e9f4572b9304f6f8.png

  接下来,我们只需把这些进度信息以进度Id做标识(progId),在SERVER端放入Java框架中的一个公有内存区即可,在浏览器中我们可使用JS以一定时间间隔访问SERVER中的某一URL,以进度Id为标识,从SERVER的公有内存区获得当前请求的进度信息。取得信息后,即可实时操控进度条运行。

  在Java框架中,公有内存区为ServletContext对象(,使用setAttribute方法,以键值对的形式将单个用户进度信息存入HashMap对象).NET框架中,公有内存区为HttpApplicationState对象。

注:向公有内存区(HashMap对象)写操作时要进行同步锁控制(synchronized),因为公有内存区可能会产生多用户(多线程)并发操作的现象。

 

问题点分析:

2.1 分段接收:

因为一次传输的大文件MultiPart数据包,字节数可能会很大(1G甚至以上),为了获取实时进度信息,以及内存开销控制,我们需要将接收过程分成多段处理,即将数据包分段循环接收(例:每次循环只接收64K数据,期间即可更新当前的进度信息)

 

2.2 完整数据包解析?/部分数据包实时解析?

  普通的解析协议方式是,将数据包全部接收后,再进行解析。以下有两种方式实现。

  数据包全部加载入内存:对于大文件的MultiPart数据量来说,这种方式会占用大量内存(比如一个用户正在上传1G的数据,那么内存区必须接收到全部1G数据后才能进行解析,如果多用户同时操作会导致服务器崩溃),这种方式不可用。

  数据包全部写入文件后再加载入内存:只能解决在接收过程中开启小内存并分段写入文件,当数据全部写入文件后,还需要加载入内存中进行整体协议分析,也会突发性导致内存开销过大,导致服务器崩溃,这种方式也不可取。

  我们这里采用的是分段接收,分段解析,分段写文件的处理方式。当数据包全部接收完毕后我们的整个分析过程也即终止,并得到用户上传的文件及其他表单信息结果。这样我们每次只需要很小的内存区(比如64K)即可完成任务。

  但这种方式会面临本次接收的分段信息内含有多个表单项信息及剩余的不完整表单信息,或本次接收的分段信息实际上不包含任何表单信息,仅仅是大文件二进制信息的一个片段。所以,这种方式在编码上会带来一定的复杂度。

 

情况1

cf7d1d75573bdf422723b91628297272.png



https://www.cnblogs.com/darkprince/p/5114936.html


 (本文提供全部源码下载,请访问 https://github.com/grayprince/UploadBigFile)


TopTop