文件上傳原理和實現

1、基本原理

上傳文件的基本流程以下圖所示。瀏覽器端提供了一個表單,在用戶提交請求後,將文件數據和其餘表單信息 編碼並上傳至服務器端,服務器端將上傳的內容進行解碼了,提取出 HTML 表單中的信息,將文件數據存入磁盤或數據庫。html


2、各過程詳解

瀏覽器編碼

<form enctype="application/x-www-form-urlencoded" action="/upload" method="post" >
		<input type="text" name="name"/><br/>
		<input type="text" name="age"/><br/>
		<input type="submit" value="上傳"/><br/>
	</form>複製代碼

在向服務器端提交請求時,瀏覽器須要將大量的數據一同提交給 Server 端, 而提交前,瀏覽器須要按照 Server 端能夠識別的方式進行編碼,對於普通 的表單數據,這種編碼方式很簡單,編碼後的結果一般是 field1=value2&field2=value2&… 的形式,如 name=ltq&age=18。一般使用的表單也是採用這種方式編碼的,Servlet 的 API 提供了對這種 編碼方式解碼的支持,只須要調用 ServletRequest 類中的方法就能夠獲得 用戶表單中的字段和數據。前端

這種編碼方式( application/x-www-form-urlencoded )雖然簡單,但對於 傳輸大塊的二進制數據顯得力不從心,對於傳輸這類數據,瀏覽器採用 了另外一種編碼方式,即 "multipart/form-data" 的編碼方式,採用這種方式, 瀏覽器能夠很容易的表單內的數據和文件一塊兒。這種編碼方式先定義好 一個不可能在數據中出現的字符串做爲分界符,而後用它將各個數據段 分開,而對於每一個數據段都對應着 HTML 頁面表單中的一個 Input 區,包 括一個 content-disposition 屬性,說明了這個數據段的一些信息,若是這個 數據段的內容是一個文件,還會有 Content-Type 屬性,而後就是數據自己。 這裏,咱們能夠編寫一個簡單的 Servlet 來看到瀏覽器究竟是怎樣編碼的。
java

實現流程:數據庫

  • 重載 HttpServlet 中的 doPost 方法
  • 調用 request.getContentLength() 獲得 Content-Length ,並定義一個與 Content-Length 大小相等的字節數組 buffer 。
  • 從HttpServletRequest 的實例 request 中獲得一個 InputStream, 並把它讀入 buffer 中。
  • 打印到控制檯,查看上傳內容。

代碼清單

  • 前端代碼

<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title></head>
 <body>	
     <form enctype="multipart/form-data" action="${pageContext.request.contextPath }/servlet/uploadServlet2" method="post" >	
	<input type="text" name="name"/><br/>
	<input type="file" name="photo"/><br/>		
        <input type="submit" value="上傳"/><br/>	
    </form>
 </body>
</html>複製代碼

  • 服務端代碼

package com.itheima.upload;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class UploadServlet1 extends HttpServlet {
   public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        int len = request.getContentLength();
        byte buffer[] = new byte[len];
        InputStream in = request.getInputStream();
        int total = 0;
        int once = 0;
        while ((total < len) && (once >=0)) {
            once = in.read(buffer,total,len);
            total += once;
        }
	System.out.println(new String(buffer,0,len));
	}
}複製代碼

在使用 數組

Chrome
 做爲瀏覽器測試時,上傳簡單的文本文件,控制檯輸出內容以下

這裏 ------WebKitFormBoundaryMsG9gupKbNVAw2Dn 就是瀏覽器指定的分界符,不一樣的瀏覽器有不一樣的肯定分界符的方法,但都須要保證分界符不會在文件內容中出現.
瀏覽器

------WebKitFormBoundaryMsG9gupKbNVAw2Dnbash

Content-Disposition: form-data; name="name"服務器


李同錢app

------WebKitFormBoundaryMsG9gupKbNVAw2Dnpost

Content-Disposition: form-data; name="photo"; filename="robots.txt"

Content-Type: text/plain


# www.robotstxt.org/


# Allow crawling of all content

User-agent: *

Disallow:


------WebKitFormBoundaryMsG9gupKbNVAw2Dn--

以上是上傳輸出內容

瀏覽器採用默認的編碼方式是 application/x-www-form-urlencoded , 能夠經過指定 form 標籤中的 enctype 屬性使瀏覽器知道此表單是用 multipart/form-data 方式編碼如:

<form enctype="multipart/form-data" action="${pageContext.request.contextPath}/servlet/uploadServlet2" method="post" >

提交請求

提交請求的過程由瀏覽器完成的,而且遵循 HTTP 協議,每個從瀏覽 器端到服務器端的一個請求,都包含了大量與該請求有關的信息, 在 Servlet 中,HttpServletRequest 類將這些信息封裝起來,便於咱們提取 使用。在文件上載和表單提交的過程當中,有兩個指的關心的問題,一是 上載的數據是是採用的那種方式的編碼,這個問題的能夠從 Content-Type 中獲得答案,另外一個是問題是上載的數據量有多少即 Content-Length , 知道了它,就知道了 HttpServletRequest 的實例中有多少數據能夠讀取 出來。這兩個屬性,咱們均可以直接從 HttpServletRequest 的一個實例 中得到,具體調用的方法是 getContentType() 和 getContentLength() 。

Content-Type 是一個字符串,在上面的例子中,增長


System.out.println(request.getContentType());

能夠獲得這樣的一個輸出字符串:


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

前半段正是編碼方式,然後半段正是分界符,經過 String 類中的方法, 咱們能夠把這個字符串分解,提取出分界符。

String contentType=request.getContentType();
int start=contentType.indexOf("boundary=");
int boundaryLen=new String("boundary=").length();
String boundary=contentType.substring(start+boundaryLen);
boundary="--"+boundary;複製代碼

解碼

通過以上的流程, 咱們能夠獲得一個包含有全部上載數據的一個字節數組和一個分界符, 而咱們要獲得如下內容:

  • 提交的表單中的各個字段以及對應的值
  • 若是表單中有 file 控件,而且用戶選擇了上傳文件, 則須要分析出字段的名稱、文件在瀏覽器端的名字、文件的 Content-Type 和文件的內容。

字節數組的內容能夠分解以下:

字節中上傳的內容可能包括file類型的和普通表單類型,兩種類型DATA存在的字段存在一些差別

具體解碼過程也能夠分爲兩個步驟:

將上傳的數據分解成數據段,每一個數據段對應着表單中的一個 Input 區。對每一個數據段,再進行分解,提出上述要求獲得的內容。

經過分界符boundary,提出DATA,

實現將boundary轉換成buffer,與傳輸的Buffer循環比較找到相同的塊,切割出DATA,

在遞歸DATA比較其中的字段,提出對應的字段,保存在集合中

https://www.ibm.com/developerworks/cn/java/fileup
相關文章
相關標籤/搜索