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

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

---解析HTTP MultiPart协议

一. 大文件上传基础描述:

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

比如:

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

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

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

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

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

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

本文最终完成图:

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

普通POST请求协议,见图:

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

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

MultiPart请求协议,见图:

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中按字节单位接收来自浏览器的数据包,所以我们也能实时的获得当前接收字节量。因此我们可以实时的获得当前传输进度百分比,用当前接收的字节量除以接收时间即可获得当前传输率(字节/秒)。

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

  • 本次数据包总字节数
  • 当前已接收的字节数
  • 本次请求发起时间
  • 当前进度节点时间
  • 当前进度状态(初始状态,接收数据中,接收数据完毕等)

  接下来,我们只需把这些进度信息以进度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:

情况2:

情况3:

三. 源码解析

3.1 项目构成要点

本次我们采用Spring框架来实现“大文件传输”功能,要点设计结构图如下:

Filter对象:

  用于负责接收MultiPart原始数据的Filter,用以在Spring内置对象之前接收用户请求。需要在Web.xml中进行配置,Web启动后,该Filter即启动,当用户请求到来时需要判断该MultiPart数据信息是否合法,接收并进行解析。

ServletInputStream/BufferedInputStream对象:

  使用以上两对象,可对本次请求进行按字节流接收。在此可创建比较小的接收缓冲区,依靠BufferedInputStream的read进行分段循环接收。

getBoundarySectFromBuf()函数:

  自定义函数,我们需要该函数从分段缓冲区中分析可能包含的多个Form表单信息,或者部分表单信息,或者二进制文件片段信息。对于表单信息分析后填充表单数据结构,对于二进制文件信息需要写文件。该函数需要完成边接收边解析边写文件的重要工作。

ProgressInfo对象:

  进度信息类,描述了一次上传请求的进度信息。该对象会用来被客户端轮询请求,以获得当前传输大文件过程中的进度信息。

FormPart对象及listFormPart集合:

  FormPart对于单个Form表单的描述。listFormPart为本次请求的全部表单描述集合。即供后续代码调用的全部表单项内容。

Controller层getProgInfo()处理函数:

  该函数将接受来自浏览器的“获得进度信息请求”,并从当前ServletContext公共内存区中找到与Progesss ID对应的进度信息对象ProgressInfo,以XML的形式返回给浏览器。该函数会被客户端轮询请求。

multi-form.jsp页面:

  本次表单的显示页面,包含多种表单项(Input,Textarea,File等)。该页面还将显示用于本次传输的进度条,传输状态,传输率等信息。页面中进度信息将使用js向服务器进行周期性轮询请求,获得及显示。

upload-result.jsp页面:

  用来显示本次请求的所有表单项信息,包括普通Input表单,及File表单信息。

3.2 重点模块解析

3.2.1 服务器端:

3.2.2 浏览器端:

(本节可参考示例代码中注释)

四. 扩展及相关

4.1断点续传:

  一般常说的断点续传是指文件下载的断点续传。 即利用HTTP协议中的Content-Range关键字(在HTTP Header中),向服务器发请求,服务器接收请求后,查看Content-Range属性的文件偏移量,从而发送后续文件二进制信息给浏览器。比如网络蚂蚁类的下载软件,即开启多线程利用Content-Range关键字将某个网络资源分布接收,最终整合保存在本地。

  而在WEB中我们所使用的上传文件断点续传功能,大多是需要下载ActiveX控件来实现。即相当于在本地下载了一个应用程序,同服务器间文件传输协议也不用使用HTTP协议,可自定义协议完成。

  利用存粹的HTTP协议进行上传文件的断点续传目前还比较少,据说利用Ajax 中的Slice方法把本地文件分成多个HTTP包POST给服务器,而服务器需要将这些包接收后并整合来实现。操作方式比较复杂,本人没尝试过,有感兴趣的朋友可深入探讨。

4.2本项目待完善要点:

  由于时间仓促,本项目目前只完成了大文件上传及进度显示的主要功能。在浏览器前端进度信息的动态显示上,前端使用的JS框架(Ext JS, JQuery)等都需要更深入的支持。

  在服务器端,也可以依靠对Filter的配置信息,对文件上传信息进行核查或过滤,比如不能上传某些扩展名的文件,文件上传尺寸控制,另存后的文件名唯一性控制等也都需要更细致的描述。

附件文件列表:

MultiData.txt :一次截获的全部MultiPart数据包信息

multi-form.jsp:多文件上传显示页面,包括获取进度信息JS脚本

upload_result.jsp:用于显示上传结果的表单项集合页面

MultiForm.java:主过滤器,Filter。用来处理全部上传过程。

UploadProgInfo.java:Controller层的Spring Bean对象,用来获取当前的进度信息。

注:基于全球开源共享理念,本人会分享更多原创及译文,让更多的IT人从中受益,与大家一起进步!

基因Cloud 原创,转发请注明出处

[email protected] (工作繁忙,有事发邮件,QQ不加,非要事勿扰,多谢!)

2015年6月2日

时间: 2024-10-05 04:58:33

【原创】用JAVA实现大文件上传及显示进度信息的相关文章

java+web+大文件上传下载

文件上传是最古老的互联网操作之一,20多年来几乎没有怎么变化,还是操作麻烦.缺乏交互.用户体验差. 一.前端代码 英国程序员Remy Sharp总结了这些新的接口 ,本文在他的基础之上,讨论在前端采用HTML5的API,对文件上传进行渐进式增强:     * iframe上传  * ajax上传  * 进度条  * 文件预览  * 拖放上传 1.1 传统形式 文件上传的传统形式,是使用表单元素file,参考 http://www.ruanyifeng.com/blog/2012/08/file_

用Struts2实现文件上传时显示进度条功能

最近在做一个资源共享的项目中,采用了Struts2.1.8+Spring2.5.6+hibernate3.32的框架整合方式进行开发.在文件上传这块,因为需要实现文件上传时显示进度条的功能,所以尝试了一下.怕以后忘记,先贴出来分享下.   要在上传文件时能显示进度条,首先需要实时的获知web服务端接收了多少字节,以及文件总大小,这里我们在页面上使用AJAX技术每一秒向服务器发送一次请求来获得需要的实时上传信息.但是当我们使用struts2后怎么在服务端获得实时的上传大小呢?这里需要用到commo

java实现大文件上传

文件上传是最古老的互联网操作之一,20多年来几乎没有怎么变化,还是操作麻烦.缺乏交互.用户体验差. 一.前端代码 英国程序员Remy Sharp总结了这些新的接口 ,本文在他的基础之上,讨论在前端采用HTML5的API,对文件上传进行渐进式增强: * iframe上传 * ajax上传 * 进度条 * 文件预览 * 拖放上传 1.1 传统形式 文件上传的传统形式,是使用表单元素file,参考 http://www.ruanyifeng.com/blog/2012/08/file_upload.h

基于 WebSocket 的聊天和大文件上传(有进度提示)完美实现

大家好,好久没有写文章了,当然不是不想写,主要是工作太忙,公司有没有网络环境,不让上网,所以写的就少了.今天是2019年的最后一天,明天就要开始新的一年,当然也希望自己有一个新的开始.在2019年的最后一天,写点东西,作为这一年的总结吧!写点啥呢?最近有时间,由于公司的需要,需要实现一个自己的.Web版本的聊天工具,当然也要能传输文件.经过两个星期的无网络.艰苦的学习,终于写出了一个最初的版本.在公司里面里面已经生成正式版本了,很多类型都进行了抽象化,支持注册,头像,私信,群聊,传输大文件,类似

Asp.Net 无刷新文件上传并显示进度条的实现方法及思路

相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦苦来 实现呢?我并不否认"拿来主义",只是我个人更喜欢凡是求个所以然.本篇将阐述通过Html,IHttpHandler和 IHttpAsyncHandler实现文件上传和上传进度的原理,希望对你有多帮助. 效果图: 本文涉及到的知识点:1.前台用到Html,Ajax,JQuery,JQuery UI 2.后台用到一般处理程序(IHttpHandler)和

java http大文件上传,断点续传项目研究,Github上传源代码

1,项目调研 因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面. https://github.com/freewebsys/java-large-file-uploader-demo 效果: 上传中,显示进度,时间,百分比. 点击[Pause]暂停,点击[Resume]继续. 2,代码分析 原始项目: https://code.google.com/p/java-lar

Lion.Web.UpLoadModule 1.0_Demo提供大文件上传并显示上传进度条2V1

Yann LeCun其人,Facebook AI研究院(FAIR)负责人,深度学习三架马车之一,卷积神经网络(CNN)之父,享受得了万人敬仰,也在无人问津的寒冬挣扎过. 他于1960年出生在巴黎,1987-1989年博士后期间拜在大神Geoffrey Hinton门下,1988年被Larry Jackel招入贝尔实验室,1989年提出在计算机视觉中使用卷积神经网络,其后此项技术被用于在自动取款机上读取支票,影响至今.1998年又提出基于梯度的学习. 或许是天性,又或许是经历过神经网络和深度学习备

java 大文件上传 断点续传 完整版实例 (Socket、IO流)

原文出自:https://blog.csdn.net/seesun2012 java两台服务器之间,大文件上传(续传),采用了Socket通信机制以及JavaIO流两个技术点,具体思路如下: 实现思路: 1.服:利用ServerSocket搭建服务器,开启相应端口,进行长连接操作 2.服:使用ServerSocket.accept()方法进行阻塞,接收客户端请求 3.服:每接收到一个Socket就建立一个新的线程来处理它 4.客:利用Socket进行远程连接,询问已上传进度 5.客:使用File

【Java】JavaWeb文件上传和下载

文件上传和下载在web应用中非常普遍,要在jsp环境中实现文件上传功能是非常容易的,因为网上有许多用java开发的文件上传组件,本文以commons-fileupload组件为例,为jsp应用添加文件上传功能.common-fileupload组件是apache的一个开源项目之一,可以从http://jakarta.apache.org/commons/fileupload/下载.用该组件可实现一次上传一个或多个文件,并可限制文件大小. 开发环境 创建一个javaweb项目,加入common-f