PHP關於檔案上傳部分,特別提到表單隱藏欄位:MAX_FILE_SIZE,意思是接收檔案的最大尺寸。文檔中給出的例子如下:
<form enctype="multipart/form-data" action="_URL_" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="30000">
Send this file: <input name="userfile" type="file">
<input type="submit" value="Send File">
</form>
這
裡設定MAX_FILE_SIZE =
30000,期待一種可能,使得瀏覽器在傳送檔案之前能夠依此作出預先判斷,如果檔案尺寸大於30000位元組,則不執行實際的POST動作。也就是不往服
務器傳送檔案內容,而是直接在用戶端提醒使用者“你試圖上傳的檔案超過30000位元組”。
這的確是一個非常棒的主張,但在現實中卻暫時無法實現。不是因為這個限制可以“被簡單地繞過”,而是IE和FireFox這兩個主流瀏覽器都不支援這個特性。PHP的這個建議尚未被採納。
MAX_FILE_SIZE還有一個用場:後台PHP會判斷已接收的檔案大小是否大於這個值,如果超出,$_FILES['thisfile']['error']會被設定為UPLOAD_ERR_FORM_SIZE(2),同時放棄儲存臨時檔案,將$_FILES['thisfile']['size']置0。
這個例子,沒問題,表現正常,當我試圖上傳一個40多K的檔案時,PHP程式報告“檔案超過MAX_FILE_SIZE”。
但是,如果我們將表單中的MAX_FILE_SIZE從30000減少到1000,情形又如何呢?
- 上傳800位元組的檔案,正常;
- 上傳40K的檔案,PHP報告檔案過大,也正常;
- 上傳3000個位元組的檔案,PHP未報告錯誤,它成功儲存了檔案!出乎意料!
問題就出在main/rfc1867.c中判斷檔案是否超長的這部分代碼上。php每次從buffer中讀取FILLUNIT位元組長度的內容後,首先判斷“已經讀到的內容長度(total_bytes)”是否大於MAX_FILE_SIZE,然後再增加“已經讀到的內容長度(total_bytes)”。這樣一來,和預計的結果之間至多會有FILLUNIT位元組的誤差,而FILLUNIT=1024*5=5K。(點擊bug瞭解詳細內容)
這就是說,當MAX_FILE_SIZE<5K時,上傳一個大於MAX_FILE_SIZE,但是小於5K的檔案是沒有問題的。
當然,因為這個設定很容易被繞過,所以伺服器端編程不應當依賴於MAX_FILE_SIZE。而且,5K到底是個很小的數值,對大多數上傳檔案的表單來說沒有影響。
btw, PHP中post_max_size,upload_max_filesize, MAX_FILE_SIZE的設定,和用戶端上傳給伺服器端的流量大小無關。Apache伺服器從用戶端接收長度不超過LimitRequestBody位元組數的請求,然後傳送給php模組,php模組再決定是否儲存成臨時檔案,設定$_FILES全域變數,移交給script進一步處理。這個Apache的LimitRequestBody選項預設值=0,允許Request body的最大位元組數是2G(Linux + Apache)