上傳文件的基本流程以下圖所示。瀏覽器端提供了一個表單,在用戶提交請求後,將文件數據和其餘表單信息 編碼並上傳至服務器端,服務器端將上傳的內容進行解碼了,提取出 HTML 表單中的信息,將文件數據存入磁盤或數據庫。html
<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
實現流程:數據庫
<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));
}
}複製代碼
在使用 數組
這裏 ------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類型的和普通表單類型,兩種類型DATA存在的字段存在一些差別
經過分界符boundary,提出DATA,
實現將boundary轉換成buffer,與傳輸的Buffer循環比較找到相同的塊,切割出DATA,
在遞歸DATA比較其中的字段,提出對應的字段,保存在集合中
https://www.ibm.com/developerworks/cn/java/fileup