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());

       }

}

相關文章
相關標籤/搜索