HTML中文件上传与上传进度跟踪的原理分析

第一部分:文件上传

 

 

在对网站实用性的贡献方面,文件上传恐怕起到了举足轻重的作用,在2009年的今天,我们几乎很难发现有哪个Web2.0风格的网站没有上传功能。当我们在博客上发布日志时,突然有一个想要贴图的想法;当我们注册某家网站的会员时,人家突然问你要照片要进行实名认证;当我们刚刚制作了一个小程序,想与网络上的朋友分享时。所有的这些都需要网页设计者为我们提供文件上传的入口,传统的FTP方式虽然效率很高,但是不幸的是,FTP并非一种很好的解决方案,一方面,FTPHTTP无法出现在同一场合(我们总不能让用户一会儿在网页界面中输入表单,一会儿又在FTP界面里上传文件吧);另一方面,让用户使用FTP无疑是一种把家门打开让外人随意出入的举动,估计还没有哪家网站有这么慷慨。因此,现在使用频率最多的还是基于HTML表单的文件上传。

HTML中实现文件上传,RFC1867规范是目前程序员的普遍选择。RFC1867为实现文件上传在HTML页面中增加了<input type=”file”>控件,并且规定了当表单用作上传时表单的enctype属性必须设置为”multipart/form-data”等,关于这些细节问题可以参考http://www.faqs.org/rfcs/rfc1867.html,这里主要讲一下RFC1867的表单数据传输格式。当服务器端的WEB应用程序用输入流的形式读取表单数据,再将数据输出到页面上。

ASP.NET或是经典ASP下我们通常的做法是输入以下代码:

<%

Response.BinaryWrite(Request.BinaryRead(Request.TotalBytes));

%>

结果可以得到形如下面的文本:

-----------------------------7d52b133509e2

Content-Disposition: form-data; name="file1"; filename="c:/aa.txt"

Content-Type: text/plain

XXXXXXXXXXXXXXXXX

-----------------------------7d52b133509e2

Content-Disposition: form-data; name="userName"

zhangsan

-----------------------------7d52b133509e2

Content-Disposition: form-data; name="password"

123

-----------------------------7d52b133509e2—

对以上文本作一些简要说明。

第一行代表的本次提交的分隔符,用于分隔不同HTML表单控件,正如上面所显示的那样,此HTML的表单中包含了3个控件,第一个是文件控件,上传了一个文件,其全路径为c:/aa.txt,文件类型为text/plain(纯文本类型的MIME编码);第二个和第三个都是普通数据控件,其键值分别是username=zhangsanpassword=123

对于如何将上面的数据转化成文件,在这里不谈具体实现方式,只阐述一下原理,因为每种编程语言都有不同的操作。首先是如何确定边界的问题,我们需要一个字符串来保存分隔符,因为分隔符是随机产生,具有不确定性。其次我们要定位文件数据的起始位置,文件控件与其它控件不同之处在于,第一行包含了filename=的字样。接下来要解决的一个问题是如何将数据流保存成文件,在这个阶段,我们的情况好比是左手拿着客户端提交给我们的输入流,右手拿着刚刚创建的文件输出流,二者都是严重消耗服务器资源的主儿,因此我们不可能采取让输入流读一个字节再让输出流写一个字节的做法,这样太慢,因此我们此时还要建立一个缓冲数组(容量一般设在2K-4K)来作为输入输出的缓冲区。

这样,实现上传就可以解决了,不过,需要说明的是,当我们采用输入流的方式读取数据时,我们不能再使用类似于Request.getParameter()或是Request.Form()的做法来读取表单了,因此提取表单的其它数据就需要我们自己编码完成了。

 


 

第二部分:上传进度跟踪

 

 

虽然我们现在可以很有成就感地在网页上随意放置上传控件,但是用户开始变得头痛了,因为他们在单击了“Upload”按钮之后一般只做一件事情“傻等”。但是站在可用性的角度来说,让用户白白等待而不告知其上传的进展状况可不是一个明智的选择,著名Web视觉设计师Penny Mcintire教授认为:假如访问者必须等待,我们应当对访问者给予提示。现在,头痛再一次回传给程序员,因为我们如果不能将进度及时告知用户,我们将损失访问量。

完成一个上传进度的获取可能是一件很容易的事情,但是将其显示出来可能需要一些努力。我们可以在服务器端建立一些变量来保存一些上传状态,首先,我们需要保存上传文件的大小,这个东西可以从HTTP头中获取;然后,我们需要一个变量来指示当前已经读取的字节数。这样,进度状态的数据获取就算基本上完成了,但是使其显示到网页中却是一件让人头痛的事情,因为B/S模式的局限性,我们不可能说运用Server Push的手段使得每当上传进度发生改变就发送到客户端来显示。所以,在确定解决方案之前,我们一定要做一些其它的准备,当然我在本文所阐述的只是我个人的愚见,我相信会有更好的处理方案。

首先,我们不能直接地去使用Response.Write(Java Servlet中是out.print)来在浏览器中显示,理由很简单:一旦我们使用了这个方法,当前浏览器的页面就会被刷掉。因此,我们需要做的是将显示进度的任务交给另一个页面来完成,这样,为了使两个页面能共享到进度的数据,我们还必须将当前上传进度保存到Session中。

接下来,我们还需要借助于Ajax<iframe>的帮忙。当我们点击了“提交”之后,页面就会开始着手转向,但我们不能让其转向,所以这时,一个隐藏的<iframe>标签可以用来作为网页转向的牺牲品,而用户完全不知道(除非是那些每天无聊到看HTML源代码的人)。而Ajax的作用也是在显示进度时重要的一环,通过在HTML中嵌入Javascript代码,使HTML每隔一段时间调用Ajax来访问那个保存进度的页面,从而及时获取当前上传的进度。

然而,不要以为到这里就结束了。我们的想法是很好,这边网页上传着文件,那边用Ajax把进度页面down下来,可是我们好像忽略了一个基本常识—Web应用程序是串行工作的,也就是说,如果上传不完成的话,即使我们发送了一个查看进度页面的请求,服务器也只是将这个请求挂入队列中,然后等到上传完成后再去处理。换句话说,我们只能是等了很长的时间,最后傻傻的看着一个100%出现在浏览器上。所以,必须要将这个处理线程化!关于多线程问题,因为各个语言的表达情况也不同,这里就不再详述。

 


 

第三部分:总结  因为我认为自己还处于Web编程中的菜鸟阶段,所以犯错是不可避免的,提出的解决方案可能也有很多缺点,在这里欢迎大家批评指正。不过,在最后,我还是想把自己做得一个比较“丑”的显示上传进度的源代码地址贴出来,希望会有一点用处。 

http://www.pudn.com/downloads162/sourcecode/web/detail734899.html


 


 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值