1. 程式人生 > >為什麼上傳檔案的表單需要設定enctype="multipart/form-data"

為什麼上傳檔案的表單需要設定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

,所以表單的內容會按URL規則編碼,然後根據表單的提交方法:

  • 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中了。

注意兩點:

  1. "hello world"被編碼為%22hello+world%22,特殊字元和空格都被編碼
  2. 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放在了正文中,其他沒有區別了

注意兩點:

  1. "hello world"被編碼為%22hello+world%22,特殊字元和空格都被編碼
  2. 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請求中。

參考文章