[转]RFC1867协议客户端实现

    技术2022-05-11  58

    1.       RFC1867协议介绍

    RFC1867协议主要是在HTTP协议的基础上为INPUT标签增加了file属性,同时限定了Formmethod必须为POSTENCTYPE必须为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

     

     

     

    请求行主要是描述请求的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)      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—

     

     

     

    很明显,增加了文件上传后,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());

           }

    }

     

     

     

    7.       附录

    请通过chen77716@hotmail.com与作者联系,转载时请注明出处。

    最新回复(0)