RFC1867协议
介绍
RFC1867协议主要是在HTTP协议的基础上为INPUT标签增加了file属性,同时限定了Form的method必须为POST,ENCTYPE必须为multipart/form-data。当然还增加了一些与此相关属性,但都不是很重要,我们在此不作讨论。 在一般的基于Web的程序中,我们往往使用<input type=”file”>标签,该标签在被浏览器解析后会产生一个文本框和一个浏览按钮,单击浏览按钮会出现系统的文件选择框。 2. 执行上传及<input type=”file”>标签的一些特性 在上图选择相应的文件,按Upload按钮即可把选择的文件上传到服务器(服务器端可用JspSmartUpload等组件接受文件)。归根结底上传的所有操作都是由浏览器作的,用户所做的只是简单地选择了一下文件而已,接下来的问题是,如何能把一个目录中所有的文件实现一次性上传? (1) 因为目录下的文件数量是不定的,因此我们基本不可能通过增加多个<input type=”file”>标签的方式来解决问题。 (2) 如果在Jsp中我们可以考虑以下方式来解决:通过Jsp动态创建<input type=”file”>标签,并使所创建的标签不可见。把每个标签的Value属性设置为每个文件的路径。在按Upload时再实行一次性上传。在我们试验了之后就会发现,对<input type=”file”>的Value属性赋值是徒劳的行为,因为RFC1867协议并没有要求浏览器的实现者一定实现Value属性,而IE恰好忽略了Value属性。 即以下代码将是徒劳的(IE中) <script language=”javascript”> //对Value赋值 Form1.file1.value=”c:\\aa.txt”; //执行后,IE将忽略此赋值 <.script>
上述两种方式均无法完成我们需要的功能,接下来我们只能剖析IE是如何完成上传功能,把具体的实现方法用ActiveX或(Applet)来完成。 3. HTTP协议的简单介绍 一般说来我们认为HTTP协议是构建在TCP/IP之上的协议,其实HTTP协议本身无此限制,但因现实中多数情况均是如此,我们就姑且如此认为。HTTP数据总体说来分三大部分: (1) 请求行,如下格式 (Request) POST SP URL SP HTTP/1.1 \r\n 请求方法+空格+请求URL+空格+HTTP协议版本+回车换行 如:POST http://localhost:8080/test/test.jsp HTTP1.1\r\n
(Response)HTTP/1.1 SP 200 SP OK \r\n HTTP协议应答版本+空格+状态码+状态描述+回车换行 如:HTTP/1.1 200 OK \r\n
请求行主要是描述请求的URL,HTTP协议版本,应答状态等信息。 (2) 请求头 在HttpServletRequest接口里已经封装了对HTTP头操作的方法。如Content-type,Content-length,User-Agent,Host等都是HTTP头。HTTP头主要描述了HTTP所传输数据的一些信息,如主机,数据内容类型,数据长度,代理类型等。 如: User-Agent: myselfHttp/1.1\r\n Accept: www/source; text/html; p_w_picpath/gif; */*\r\n HTTP头+:+空格+头信息+回车换行 (3) HTTP实体 HTTP实体存放着,HTTP请求的内容,如参数信息,文本框的内容,隐含控件的值,ListBox的值等。如果在页面上存在: <input type=”text” name=”userName” value=”zhangsan”> <input type=”password” name=”password” value=”123”> HTTP实体会出现以下形式:(POST提交) userName=zhangsan&password=123 GET提交的时候需要解析HTTP请求行中的URL,在此不多作讨论。
4. RFC1867协议的数据格式 (1) RFC1867对HTTP头的变更 RFC1867对HTTP头作了适当地变更,但变更很小。首先content-type头由以前的: content-type: application/x-www-form-urlencoded 变为 content-type: multipart/form-data; +空格+ boundary=---------------------------7d52b133509e2
即增加了boundary,所谓的boundary其实就是分割线,下文将看到,RFC1867利用boundary分割HTTP实体数据。boundary中数字字符区是随机生成的。
(2) 对HTTP实体的变更 因为RFC1867增加了文件上传得功能,而上传文件内容自然也会被加入到HTTP的实体中。现在因为既有HTTP一般的参数实体,又有上传文件的实体,所以用boundary把每种实体进行了分割,HTTP的实体看起来将是下面的样子:
-----------------------------7d52b133509e2 Content-Disposition: form-data; name="file1"; filename="c:\aa.txt" Content-Type: text/plain
文件内容在此处 -----------------------------7d52b133509e2 Content-Disposition: form-data; name="userName"
zhangsan -----------------------------7d52b133509e2 Content-Disposition: form-data; name="password"
123 -----------------------------7d52b133509e2—
{关于实体的其他说明: Content-type: multipart/form-data, boundary=AaB03x (\r\n) --AaB03x //boundary } 很明显,增加了文件上传后,HTTP实体变得稍微复杂了,首先是通过boundary把实体分开,以便于读取,然后对FileUpload的格式也作了限制。 (3) RFC1867协议的数据格式 根据RFC1867协议,在HTTP实体中必须对每个上传得文件有说明头,如: Content-Disposition: form-data; name="file1"; filename="c:\aa.txt"
Content-Disposition:指明内容类型是form-data name="file1":指明页面上<input type=”file”>标签的名字是file1 filename="c:\aa.txt":指明上传文件在客户端上的全路径 空行:文件头说明完毕后,要加一空行,以表示后面的数据是文件的内容 文件内容:再接下来就是文件的内容
从这个角度说,完全可以利用HTTP协议+RFC1867协议开发基于文档管理应用程序。 5. 协议的实现(客户端) 协议的好处就是,只要你提供的数据符合协议的要求,Server端就可以正确解析你的请求。而不论数据是由IE产生的,或有你自己的Application产生的。通过上面的分析,我们已经基本清楚了RFC1867协议的 要求,只要我们打开指定的端口,把数据按照协议的要求写进去就会模拟出IE上传的功能。用程序实现是非常Easy的事。附件将给出Java实现版本,程序 只是简单地实现了上传,根据我们前面的分析实现文件上传,参数传递这种稍麻烦的形式也是比较简单的。另外,该程序并没有实现返回数据的解析,同样根据我们 前面的分析,按照HTTP协议去解析返回的数据也不是难事。总之,希望本程序能起到抛砖引玉的作用,
6.
代码实现
|
在最初的 http 协议中,没有上传文件方面的功能。 rfc1867 ( http://www.ietf.org/rfc/rfc1867.txt ) 为 http 协议添加了这个功能。客户端的浏览器,如 Microsoft IE, Mozila, Opera 等,按照此规范将用户指定的文件发送到服务器。服务器端的网页程序,如 php, asp, jsp 等,可以按照此规范,解析出用户发送来的文件。
Microsoft IE, Mozila, Opera 已经支持此协议,在网页中使用一个特殊的 form 就可以发送文件。
绝大部分 http server ,包括 tomcat ,已经支持此协议,可接受发送来的文件。
各种网页程序,如 php, asp, jsp 中,对于上传文件已经做了很好的封装。
1. 在一个 html 网页中,写一个如下的form :
现在第三方的 http upload file 工具库很多。Jarkata 项目本身就提供了fileupload 包 http://jakarta.apache.org/commons/fileupload/ 。文件上传、表单项处理、效率问题基本上都考虑到了。在 struts 中就使用了这个包,不过是用 struts 的方式另行封装了一次。这里我们直接使用 fileupload 包。至于struts 中的用法,请参阅 struts 相关文档。
这个处理文件上传的 servelet 主要代码如下:
假设我们要发送一个二进制文件、一个文本框表单项、一个密码框表单项。文件名为 E:\s ,其内容如下:(其中的XXX代表二进制数据,如 01 02 03)
注意:Content-Length: 424 这里的424是红色内容的总长度(包括最后的回车)
注意这一行:
Content-Type: multipart/form-data; boundary=---------------------------7d33a816d302b6
根据 rfc1867, multipart/form-data是必须的.
---------------------------7d33a816d302b6 是分隔符,分隔多个文件、表单项。其中33a816d302b6 是即时生成的一个数字,用以确保整个分隔符不会在文件或表单项的内容中出现。前面的 ---------------------------7d 是 IE 特有的标志。 Mozila 为---------------------------71
用手工发送这个例子,在上述的 servlet 中检验通过。
注意 enctype="multipart/form-data", method=post, type="file" 。根据 rfc1867, 这三个属性是必须的。multipart/form-data 是新增的编码类型,以提高二进制文件的传输效率。具体的解释请参阅 rfc1867
转载于:https://blog.51cto.com/sensen/720869