1. 程式人生 > >檔案上傳為什麼要用 enctype="multipart/form-data" ???

檔案上傳為什麼要用 enctype="multipart/form-data" ???

在檔案上傳的過程中發現,HTML表單需要設定enctype="multipart/form-data"這個屬性,雖然不這麼設定的確無法上傳,但這是為什麼呢?

HTML表單如何打包資料檔案是由enctype這個屬性決定的。enctype有以下幾種取值:

application/x-www-form-urlencoded在傳送前編碼所有字元(預設)(空格被編碼為’+’,特殊字元被編碼為ASCII十六進位制字元)
multipart/form-data 不對字元編碼。在使用包含檔案上傳控制元件的表單時,必須使用該值。
text/plain 空格轉換為 “+” 加號,但不對特殊字元編碼。
預設enctype=application/x-www-form-urlencoded,所以表單的內容會按URL規則編碼,然後根據表單的提交方法:

method=’get’ 編碼後的表單內容附加在請求連線後
method=’post’ 編碼後的表單內容作為post請求的正文內容
我們通過抓包軟體來分析一下這幾種方式產生的請求的差別

實驗一
條件

method=‘get’
enctype=application/x-www-form-urlencoded
對應的html程式碼為:

文字框中輸入"hello world",選擇檔案,點選提交,瀏覽器發出的HTML包為:

GET /xxx?name=%22hello+world%22&file=temp.png&submit=submit HTTP/1.1
Host: hello.app
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/

;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Referer: http://hello.app/formtest.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

因為是get請求,所以只有頭部沒有正文。請求的連結為/xxx?name=hello+world.&file=temp.png&submit=submit,可以看到表單的資訊已經被編碼到URL中了。

注意兩點:

"hello world"被編碼為%22hello+world%22,特殊字元和空格都被編碼
type='file’提交的檔案內容並沒有被提交,只是把檔名編碼到了URL中
實驗二
條件

method=‘post’
enctype=application/x-www-form-urlencoded
對應的html程式碼為:

文字框中輸入"hello world",選擇檔案,點選提交,瀏覽器發出的HTML包為:

POST /xxx HTTP/1.1
Host: hello.app
Connection: keep-alive
Content-Length: 50
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Origin: http://hello.app
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Referer: http://hello.app/formtest.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

name=%22hello+world%22&file=temp.png&submit=submit

與get請求相比,只是把name=hello+world.&file=temp.png&submit=submit放在了正文中,其他沒有區別了

注意兩點:

"hello world"被編碼為%22hello+world%22,特殊字元和空格都被編碼
type='file’提交的檔案內容並沒有被提交,只是把檔名編碼到了正文中
實驗三
條件

method=‘get’
enctype=‘multipart/form-data’
對應的html程式碼為:

文字框中輸入"hello world",選擇檔案,點選提交,瀏覽器發出的HTML包為:

GET /xxx?name=%22hello+world%22&file=temp.png&submit=submit HTTP/1.1
Host: hello.app
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Referer: http://hello.app/formtest.html
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

結果和實驗一一模一樣,說明get和multipart/form-data配合無效。

實驗四
條件

method=‘post’
enctype=multipart/form-data
對應的html程式碼為:

文字框中輸入"hello world",選擇檔案,點選提交,瀏覽器發出的HTML包為:

POST /xxx HTTP/1.1
Host: hello.app
Connection: keep-alive
Content-Length: 3695
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
Origin: http://hello.app
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIZDrYHwuf2VJdpHw
Referer: http://hello.app/formtest.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name=“name”

“hello world”
------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name=“file”; filename=“temp.png”
Content-Type: image/png

.PNG
.

IHDR…
…Y…/…,+|. a I k . v . . . G ? . . . P . P , , . . . m . . e . 2.... v . 7. p H Y s . . . aIk.v...G?...P.P,,...m..e.2....v.7. pHYs...%...%.IR …|IDAT(.cTT…:.?..}.(.PdA..V...L...?..#.....4.o..LS.....W.d.?...A8..LS...(.u.......D.b......b.....o&..;..<.1......IEND.B.
------WebKitFormBoundaryIZDrYHwuf2VJdpHw
Content-Disposition: form-data; name=“submit”

submit
------WebKitFormBoundaryIZDrYHwuf2VJdpHw–

這次與前兩次相比有很大的不同。請求主題的內容複雜很多。

根據boundary定義的字串,正文被分割為幾個部分,每個部分與表單中的內容一一對應。

每部分內容,還會由Content-Disposition: form-data; name="name"這樣的字串指定內容,與名字。

對於檔案內容,還有有額外的兩個欄位filename=“temp.png”‘和Content-Type: image/png,並且檔案的內容就直接附加在後面。

總結
所以,只有使用enctype=“multipart/form-data”,表單才會把檔案的內容編碼到HTML請求中。