RFC1867协议客户端实现

RFC1867协议做为HTTP协议的附加协议,详细描述了File Upload的规则。本文主要内容是给出一个RFC1867协议的客户端实现(服务器端的实现如,JspSmartUploadFileUpload等组件都已比较成熟,这里很少做介绍)。javascript

 

 

1.       RFC1867协议介绍html

RFC1867协议主要是在HTTP协议的基础上为INPUT标签增长了file属性,同时限定了Formmethod必须为POSTENCTYPE必须为multipart/form-data。固然还增长了一些与此相关属性,但都不是很重要,咱们在此不做讨论。java

在通常的基于Web的程序中,咱们每每使用<input type=”file”>标签,该标签在被浏览器解析后会产生一个文本框和一个浏览按钮,单击浏览按钮会出现系统的文件选择框。其经典表示以下图所示。浏览器

              

2.       执行上传及<input type=”file”>标签的一些特性服务器

在上图选择相应的文件,按Upload按钮便可把选择的文件上传到服务器(服务器端可用JspSmartUpload等组件接受文件)。归根结底上传的全部操做都是由浏览器做的,用户所作的只是简单地选择了一下文件而已,接下来的问题是,如何能把一个目录中全部的文件实现一次性上传?app

(1)        由于目录下的文件数量是不定的,所以咱们基本不可能经过增长多个<input type=”file”>标签的方式来解决问题。socket

(2)        若是在Jsp中咱们能够考虑如下方式来解决:经过Jsp动态建立<input type=”file”>标签,并使所建立的标签不可见。把每一个标签的Value属性设置为每一个文件的路径。在按Upload时再实行一次性上传。在咱们试验了以后就会发现,对<input type=”file”>Value属性赋值是徒劳的行为,由于RFC1867协议并无要求浏览器的实现者必定实现Value属性,而IE刚好忽略了Value属性。jsp

即如下代码将是徒劳的(IE中)this

<script language=”javascript”>url

         //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

 

 

请求行主要是描述请求的URLHTTP协议版本,应答状态等信息。

(2)        请求头

HttpServletRequest接口里已经封装了对HTTP头操做的方法。如Content-typeContent-lengthUser-Agent,Host等都是HTTP头。HTTP头主要描述了HTTP所传输数据的一些信息,如主机,数据内容类型,数据长度,代理类型等。

如:

User-Agent: myselfHttp/1.1/r/n

Accept: www/source; text/html; image/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)      RFC1867HTTP头的变动

RFC1867HTTP头做了适当地变动,但变动很小。首先content-type头由之前的:

content-typeapplication/x-www-form-urlencoded

变为

content-typemultipart/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—

 

 

很明显,增长了文件上传后,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协议去解析返回的数据也不是难事。总之,但愿本程序能起到抛砖引玉的做用,关于RFC1867更深刻的实现或应用,请跟做者联系。

 

 

6.       代码实现


 

 

    import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.Socket;

 

 

public class HttpClient {

   

    private String boundary =

                  "---------------------------7d51372f1c05a8";

       private String contentType = "multipart/form-data;

                                   boundary="+boundary;

      

       private static final byte CR = (byte)'/r';

       private static final byte LF = (byte)'/n';

       private static final byte[] CRLF = new byte[]{CR,LF};

      

       private Socket socket;

       private String host;

       private int port;

      

       public static void main(String[] args) {

              try{

              HttpClient client = new HttpClient("localhost",8080);

                    

                     //upload file array

                     File[] files = new File[1];

                     for(int i=0;i<files.length;i++){

                            files[i] = new File("d://aa.txt");

                     }

                    

                     client.uploadFile(files);

                    

              }catch(Exception e){

                     e.printStackTrace();

              }

       }

      

       public HttpClient(String host,int port){

              this.host = host;

              this.port = port;

       }

      

       private void openServer() throws Exception {

              socket = new Socket(host,port);

       }

      

       private void closeServer() throws Exception {

              if(socket!=null){

                     socket.close();

              }

       }

      

       private void addHead(int contentLength,OutputStream out) throws

                                                        IOException {

             

              //request line,end withd CRLF

              write(out,"POST http://localhost:8080/test1/upload/1 HTTP/1.1");

              out.write(CRLF);

                           

              //request head fields

              write(out,"User-Agent: SysmitAgent");

              out.write(CRLF);

             

              write(out,"Host: localhost:8080");

              out.write(CRLF);

             

              write(out,"Accept: www/source; text/html; image/gif; */*");   

              out.write(CRLF);

                    

              write(out,"Accept-Encoding: gzip, deflate");

              out.write(CRLF);

             

              //entity head fields

              write(out,"Content-Type: "+contentType);

              out.write(CRLF);

             

              write(out,"Content-Length: "+contentLength);

              out.write(CRLF);

             

              out.write(CRLF);

       }

      

       private void addBody(File file,OutputStream out) throws IOException {

              //write boundary

              write(out,boundary);

              out.write(CRLF);

             

              //write file info

              String disposition = "Content-Disposition:"

                                   +" form-data;"

                                   +" name=/"file1/";"

                                   +" filename=/""

+file.getAbsolutePath()+"/"";

             

              write(out,disposition);

              out.write(CRLF);

             

              //write file content type info

              write(out,"Content-Type: text/html");

              out.write(CRLF);

             

              //write SP(empty line)

              out.write(CRLF);

             

              //write file content

              InputStream is = new FileInputStream(file);

              byte[] b = new byte[1024];

              int count = is.read(b);

              while(count!=-1){

                     out.write(b,0,count);

                     count = is.read(b);

              }

             

              is.close();

             

              //write crlf

              out.write(CRLF);

       }

      

       public void uploadFile(File[] files) throws Exception {

             

              //open server

              openServer();

             

              //open stream

              OutputStream out = socket.getOutputStream();

             

              ByteArrayOutputStream bos = new ByteArrayOutputStream();

             

              for(int i=0;i<files.length;i++){

                    

                     addBody(files[i],bos);

              }

             

              write(bos,boundary+"--");

              bos.write(CRLF);

             

              addHead(bos.size(),bos);

              bos.writeTo(out);

              bos.close();

              out.flush();

             

              //close server

              closeServer();

       }

      

       private void write(OutputStream out,String msg) throws IOException

       {

              out.write(msg.getBytes());

       }

}

相关文章
相关标签/搜索