web文件上傳下載原理淺析

一、web文件上傳淺析

現在有很多Web程序都有上傳功能,實現上傳功能的組件或框架也很多,如基於javaCommons FileUpload、還有Struts1.xStruts2中帶的上傳文件功能(實際上,Struts2在底層也使用了Commons FileUpload)。在asp.net中也有相應的上傳文件的控件。

雖然現在有很多上傳組件可以利用,但是瞭解Web上傳文件的原理,對於處理突然出現的問題會有很大的幫助,下面就來講一下通過瀏覽器上傳文件的基本原理。在瞭解了原理之後,就可以非常容易地自制滿足自身需要的上傳組件了。

衆所周知,在客戶端代碼中需要使用<input type='file' name='file' />來選擇要上傳的文件,並上傳,代碼如上:

<html>
    <head>
        <title>upload</title>
        <meta http-equiv="description" content="this is my page">
        <meta http-equiv="content-type" content="text/html; charset=GB18030">
    </head>

    <body>
        <form action="servlet/UploadFile" method="post"
            enctype="multipart/form-data">
            <input type="file" name="file1" id="file1" />
            <input type="file" name="file2" id="file2" />
            <input type="submit" value="上傳" />
        </form>
    </body>
</html>
   從上面的代碼可以看出,有兩個文件選擇框(file1file2),在上傳文件時,<form>標籤必須加上enctype="multipart/form-data",否則瀏覽器無法將文件內容上傳到服務端。下面我們來做個實驗。在ServletdoPost方法中編寫如下的代碼,如果想使用asp.net或其他的語言或技術,也可以很容易實現相應的功能。

 public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException
    {
        java.io.InputStream is = request.getInputStream();
        java.io.FileOutputStream fos = new java.io.FileOutputStream("d:\\out.txt");
        
        byte[] buffer = new byte[8192];
        int count = 0;
        while((count = is.read(buffer)) >0)
        {
            fos.write(buffer, 0, count);
        }        
        fos.close();
    }
   上面的功能非常簡單,只是通過request獲得一個InputStream對象,並通過這個對象從客戶端獲得發送過來的字節流(注意,一定要用字節流,因爲,上傳的文件可能是二進制文件,如圖象文件,因此,使用字節流會更通用)。並將這些字節流保存在D盤的out.txt文件中。然後我們打開out.txt,文件的內容如圖1所示:
           
 

                                                                                                                           圖1

 由於out.txt是使用文本形式打開的,並且file1上傳的是a.jpg(一個圖象文件),因此,顯示的是一些亂碼。我們可以不用管它們。只需要看看這些內容的頭部。我們很快就可以找到規律。每一個文件內容的頭部都由“-----------------------------30514443229777”分隔,然後是這個文件的屬性,如下:

Content-Disposition: form-data; name="file1"; filename="a.jpg"

Content-Type: image/jpeg

其中包含了文件選擇框的name屬性,還有上傳的文件名(filename字段),要注意的,firefox在上傳時,這個filename屬性值只是文件名,如果使用IE,就是帶路徑的文件名,如D:"a.jpg

接下來的規則就和HTTP的頭一樣了,以一個空行("r"n)分隔。後面就是文件的具體內容。現在最關鍵的文件的結尾,從圖1可以看出,文件的結尾也是“-----------------------------30514443229777”,因此,可以斷定,第一個上傳的文件(包括文件頭)是夾在兩個“-----------------------------30514443229777”之間的。而“-----------------------------30514443229777”就是multipart/form-data協議的分隔符。但這裏還有一個最關鍵的問題。這個分隔符每次上傳都不一樣,服務端是如何知道每次上傳的這個分隔符的呢?

實際上,這個分隔符是通過HTTP請求頭的Content-Type字段獲得,可通過下面的代碼輸出這個字段值:

System.out.println(request.getHeader("Content-type"));

輸出的內容如下:

multipart/form-data; boundary=---------------------------106712230227687

只要在服務端獲得boundary後面的值即可。經過測試,Content-Type中的分隔符號中的“-”比實際上傳的“-”少兩個,不知是怎麼回事。不過這沒關係,我們可以認爲每一個文件塊是以""r"n—“結尾的,或是直接將從boundary獲得的分隔符加兩個“”。而最後結尾的分隔符是“---------------------------106712230227687—”,後面多了兩個“”。

綜合上述,也就是說,一個文件塊是以“---------------------------106712230227687”開頭,以“”結尾,從圖2可以看出這一切。

                                         

                                                                                                                         圖 2

至於剩下的工作,就是按着上面的規則來分析這些字符流了。分析的方法很多。在這裏就不詳述了。

      multipart/form-data規 範原文:http://www.ietf.org/rfc/rfc2388.txt 
   Form-based File Upload in HTML:http://www.ietf.org/rfc/rfc1867.txt

二、web文件下載淺析

文件下載流程

服務器端編碼原理下載分析:在服務端通過response可以獲取到輸出流,在服務端通過輸入流將文件流獲取到然後通過response獲取輸出流直接寫回到瀏覽器即可完成下載。

服務器端下載兩個響應頭設置

  1. 怎樣能通知瀏覽器,下載文件是什麼:通過response.setContentType設置響應數據的mimeType類型。獲取一個文件的mimeType類型,ServletContext.getMimeType(String filename)
  2.  設置下載文件名稱:response.setHeader("Content-Disposition","attachement;filename=下載名稱")

下載亂碼問題

對於下載時,我們在顯示文件下載名稱的時候,如果包含了中文,就可能出現亂碼問題。原因在於不同的瀏覽器,他們在處理下載文件的時候編碼不一致,ie瀏覽器使用的是utf-8編碼,而firefox使用的是base64編碼。所以在下載還需要設置編碼格式。示例如下:
		response.setCharacterEncoding("utf-8");
		String filename = request.getParameter("filename");
		String downPath = path+"/upload";
		FileInputStream in = new FileInputStream(downPath+File.separator+filename);

		if(request.getHeader("user-agent").toLowerCase().contains("msie")){
			filename = URLEncoder.encode(filename, "UTF-8");
		}else{
			filename = new String(filename.getBytes("UTF-8"),"iso-8859-1");
		}
		response.setHeader("content-disposition", "attachment;filename="+filename);
		response.setHeader("content-type",this.getServletContext().getMimeType(filename));

		OutputStream os = response.getOutputStream();
		byte[] buffer = new byte[1024];
		int len=0;      
		while((len=in.read(buffer))!=-1) {
			os.write(buffer, 0, len);
		}
		in.close();
		os.close(); 

轉載地址:https://www.cnblogs.com/nokiaguy/archive/2008/05/29/1209858.html




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章