對於這塊知識點,我一直都是模糊的,不是很是清楚的。在平時的工做中,遇到上傳的問題,也沒有深刻的去研究過,也都是直接用別人封裝好的類來完成本身的工做。某一天,看了本書,說到這個知識點,一臉茫然,覺的有必要去深刻的學習一下,至少要讓本身明白HTTP文件上傳的過程,這個原理,以便未來出現問題,也能經過原理進行深刻的分析,而不是等問題來了,兩眼一抹黑,而後漫無目的的百度。哦,若是是那樣子,那該是多麼的痛苦,多麼的無助。html
因此查缺補漏,以避免讓未來的本身感到無助、痛苦,甚至難堪,走起!!!前端
咱們在開發的時候,當要用到文件上傳功能時,前端開發人員都會告訴你如下幾條金科律令:java
post
;<input type="file"/>
;enctype="multipart/form-data"
。是的,咱們必須按照上面這三條鐵令進行設定,不然就沒法上傳文件。好比咱們通常會這麼寫:web
<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <html> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <body> <form action="<%=request.getContextPath() %>/UploadServletDemo" enctype="multipart/form-data" method="post"> 上傳用戶:<input type="text" name="username"><br/> 上傳文件1:<input type="file" name="file1"><br/> 上傳文件2:<input type="file" name="file2"><br/> <input type="submit" value="提交"> </form> </body> </html>
我這裏寫了一個簡單的Servlet:瀏覽器
@WebServlet("/UploadServletDemo") public class UploadServletDemo extends HttpServlet{ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8"); // 獲取表單(POST)數據 ServletInputStream in = request.getInputStream();//此方法獲得全部的提交信息,不單單隻有內容 // 轉換流 InputStreamReader inReaser = new InputStreamReader(in); // 緩衝流 BufferedReader reader = new BufferedReader(inReaser); String str = null; while ((str=reader.readLine()) != null){ System.out.println(str); } } }
咱們把程序跑起來,而後經過Fiddler進行抓包,能夠看到咱們發送的Post請求中,請求體中有如下這樣的數據:服務器
POST http://localhost:8080/javawebservlet_war/UploadServletDemo HTTP/1.1 Host: localhost:8080 Connection: keep-alive Content-Length: 446 Cache-Control: max-age=0 Origin: http://localhost:8080 Upgrade-Insecure-Requests: 1 Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqj67FUBQUHXZj78G User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3 Referer: http://localhost:8080/javawebservlet_war/ Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,und;q=0.7,zh-TW;q=0.6 Cookie: JSESSIONID=6BE280EF3CBE213F73430FFDF015DE97 ------WebKitFormBoundaryqj67FUBQUHXZj78G Content-Disposition: form-data; name="username" abc ------WebKitFormBoundaryqj67FUBQUHXZj78G Content-Disposition: form-data; name="file1"; filename="文件1.txt" Content-Type: text/plain ABC文件1 ------WebKitFormBoundaryqj67FUBQUHXZj78G Content-Disposition: form-data; name="file2"; filename="文件2.txt" Content-Type: text/plain BDF文件2 ------WebKitFormBoundaryqj67FUBQUHXZj78G--
到這裏,咱們就大概就知道了HTTP上傳文件的原理了。HTTP把須要上傳的表單的全部數據按照必定的格式存放在請求體中,對於文件也是一樣的。app
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryqj67FUBQUHXZj78G
表示要上傳附件,其中boundary
表示分隔符,若是表單中有多項,就要使用boundary
進行分隔,每一個表單項由------FormBoundary
開始,以------FormBoundary
結尾。例如這樣:dom
------FormBoundary Content-Disposition: form-data; name="param1" value1 ------FormBoundary
這個boundary
的值是由瀏覽器生成的,由瀏覽器來保證與上傳內容不重複。在每一個分隔項裏,須要咱們去重點關注Content-Disposition
消息頭,其中第一個參數老是固定不變的form-data
,name表示表單元素屬性名,回車換行符後面的內容就是元素的值。還有Content-Type
表示咱們上傳的文件的MIME類型,咱們在服務器端須要根據這個進行文件的區分。ide
HTTP就是按照這種格式,把表單中的數據封裝成一個請求一股腦的發給服務器端,服務器端根據這種規則對接收到的請求進行解析,從而完成文件上傳功能。post
最後一個
boundary
的結尾會多兩個--
經過上面的描述,咱們能夠知道完成文件上傳功能,重點工做不是在於客戶端,而是在於服務器端。服務器端須要根據客戶端發送過來的請求,根據上面說的規則對請求報文進行解析,從而提取出上傳的文件內容。能夠看到,雖然上面的規則比較簡單,可是用不一樣的開發語言來一次性寫出沒有Bug的解析程序,也不是那麼簡單的。並且這種實現過一遍,就能夠你們共享的東西就很是適合開發成組件供你們一塊兒使用,因此呢,開源社區就開發了這樣的一個組件,這個組件來給咱們完成了上面規則的編碼,而咱們須要作的就是去學會使用這個組件,就這麼簡單!
下面就經過一個簡單的FileUpload
Demo程序來總結一下如何使用FileUpload
組件。
下面來一段簡單的使用Demo。
@WebServlet("/FileUpload") public class FileUploadDemo extends HttpServlet { @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DiskFileItemFactory fac = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(fac); upload.setFileSizeMax(10 * 1024 * 1024); upload.setSizeMax(20 * 1024 * 1024); if (upload.isMultipartContent(request)) { try { List<FileItem> list = upload.parseRequest(request); for (FileItem item : list) { if (item.isFormField()) { String fileName = item.getFieldName(); String value = item.getString("UTF-8"); System.out.println(fileName + ":" + value); } else { String name = item.getName(); String id = UUID.randomUUID().toString(); name = id + name; String realPath = getServletContext().getRealPath("/upload"); File file = new File(realPath, name); item.write(file); item.delete(); } } } catch (Exception e) { e.printStackTrace(); } } else { System.out.println("不處理!"); } } }
這篇文章對經過HTTP協議進行文件上傳原理進行了比較詳細的分析和總結,但願對你們有幫助!
2019年7月31日 於內蒙古呼和浩特。