為什麼上傳檔案的表單需要設定enctype="multipart/form-data"
在學習PHP檔案上傳的過程中發現,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
- method=’get’ 編碼後的表單內容附加在請求連線後
- method=’post’ 編碼後的表單內容作為post請求的正文內容
我們通過抓包軟體來分析一下這幾種方式產生的請求的差別
實驗一
條件
method='get'
enctype=application/x-www-form-urlencoded
對應的html程式碼為:
<form action="xxx" method="get">
<input type="text" name="name">
<input type="file" name="file"/>
<input type="submit" value="submit" name="submit">
</form>
文字框中輸入"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程式碼為:
<form action="xxx" method="
post">
<input type="text" name="name">
<input type="file" name="file"/>
<input type="submit" value="submit" name="submit">
</form>
文字框中輸入"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程式碼為:
<form action="xxx" method="
get" enctype="multipart/form-data">
<input type="text" name="name">
<input type="file" name="file"/>
<input type="submit" value="submit" name="submit">
</form>
文字框中輸入"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程式碼為:
<form action="xxx" method="
post" enctype="multipart/form-data">
<input type="text" name="name">
<input type="file" name="file"/>
<input type="submit" value="submit" name="submit">
</form>
文字框中輸入"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../..,+|.$aIk.v...G?...P.P,,...m..e.2....v.7. pHYs...%...%.IR$....|IDAT(.cTT....................:.?.......}.(.Pd`A..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請求中。