HTTP文件上傳原理

前言

對於這塊知識點,我一直都是模糊的,不是很是清楚的。在平時的工做中,遇到上傳的問題,也沒有深刻的去研究過,也都是直接用別人封裝好的類來完成本身的工做。某一天,看了本書,說到這個知識點,一臉茫然,覺的有必要去深刻的學習一下,至少要讓本身明白HTTP文件上傳的過程,這個原理,以便未來出現問題,也能經過原理進行深刻的分析,而不是等問題來了,兩眼一抹黑,而後漫無目的的百度。哦,若是是那樣子,那該是多麼的痛苦,多麼的無助。html

因此查缺補漏,以避免讓未來的本身感到無助、痛苦,甚至難堪,走起!!!前端

HTTP上傳原理

咱們在開發的時候,當要用到文件上傳功能時,前端開發人員都會告訴你如下幾條金科律令: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的結尾會多兩個--

FileUpload組件

經過上面的描述,咱們能夠知道完成文件上傳功能,重點工做不是在於客戶端,而是在於服務器端。服務器端須要根據客戶端發送過來的請求,根據上面說的規則對請求報文進行解析,從而提取出上傳的文件內容。能夠看到,雖然上面的規則比較簡單,可是用不一樣的開發語言來一次性寫出沒有Bug的解析程序,也不是那麼簡單的。並且這種實現過一遍,就能夠你們共享的東西就很是適合開發成組件供你們一塊兒使用,因此呢,開源社區就開發了這樣的一個組件,這個組件來給咱們完成了上面規則的編碼,而咱們須要作的就是去學會使用這個組件,就這麼簡單!

下面就經過一個簡單的FileUploadDemo程序來總結一下如何使用FileUpload組件。

FileUpload Demo

下面來一段簡單的使用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日 於內蒙古呼和浩特。

相關文章
相關標籤/搜索