天天看點

PHP檔案上傳源碼分析(RFC1867)

檔案上傳,一般分為倆種方式FTP和HTTP, 對于我們的網際網路應用來說: FTP上傳雖然傳輸穩定, 但是易用性和安全性都是個問題. 你總不至于在使用者要上傳頭像的時候告訴使用者”請打開FTP用戶端,上傳檔案到http://www.laruence.com/uploads/中, 并以2dk433423l.jpg命名”吧?

而基于HTTP的上傳,相對來說易用性和安全性上就比FTP要增強了很多. 可以應用的上傳方式有PUT, WEBDAV, 和RFC1867三種, 本文将分析在PHP中,是如何基于RFC1867實作檔案上傳的.

RCF1867是Form-based File Upload in HTML标準協定, RFC1867标準對HTML做出了兩處修改:

 

  

另外,本标準還定義了一種新的mime類型:multipart/form-data,以及當處理一個帶有enctype=”multipart/form-data” 并且/或含有<input type=”file”>的标記的表單時所應該采取的行為。

舉例來說,當HTML想讓使用者能夠上傳一個或更多的檔案時,他可以這麼寫:

這個表單, 大家一定不陌生, 而對于PHP來說, 它自己另外定義了一個預設表單元素MAX_FILE_SIZE, 使用者可以通過這個隐藏的表單元素來建議PHP最多隻容許上傳檔案的大小, 比如對于上面的例子, 我們希望使用者上傳的檔案不能大于5000(5k)位元組, 那麼可以如下寫:

姑且不說, 這個MAX_FILE_SIZE是多麼的不可靠(是以基于浏覽器的控制,都是不可靠的), 單純從實作來講, 我會慢慢介紹這個MAX_FILE_SIZE是如何起作用的.

當使用者選擇了一個檔案(laruence.txt), 并填寫好檔案描述(”laruence的個人介紹”), 點選上傳後, 發生了什麼呢?

在使用者确定送出以後, 浏覽器會發送如下類似格式的資料包到form中action屬性指定的頁面(在本例中是upload.php):

接下來, 就是伺服器, 是如何處理這些資料了.

這個時候, PHP會調用sapi_activate來初始化一個請求, 在這個過程中, 首先判斷請求類型, 此時是POST, 進而去調用sapi_read_post_data, 通過Content-type, 找到rfc1867的處理函數rfc1867_post_handler, 進而調用這個handler, 來分析POST來的資料.

然後, PHP通過boundary, 對于每一個分段, 都通過檢查, 是否同時定義了:

進而進行不同的處理.

在這個過程中, PHP會去檢查普通資料中,是否有MAX_FILE_SIZE.

有的話, 就會按照它的值來檢查檔案大小是否超出.

通過上面的代碼,我們也可以看到, 判斷分為倆部, 第一部分是檢查PHP預設的上傳上限. 第二部分才是檢查使用者自定義的MAX_FILE_SIZE, 是以表單中定義的MAX_FILE_SIZE并不能超過PHP中設定的最大上傳檔案大小.

通過對name和filename的判斷, 如果是檔案上傳, 會根據php的設定, 在檔案上傳目錄中建立一個随機名字的臨時檔案:

傳回檔案句柄, 和臨時随機檔案名.

之後, 還會有一些驗證,比如檔案名合法, name合法等.

如果這些驗證都通過, 那麼就把内容讀入, 寫入到這個臨時檔案中.

當循環讀入完成後, 關閉臨時檔案句柄. 記錄臨時變量名:

并且生成FILE變量, 這個時候, 如果是有名上傳, 那麼就會設定:

如果是無名上傳, 則會使用tmp_name來設定:

最終交給使用者編寫的upload.php處理.

這時在upload.php中, 使用者就可以通過move_uploaded_file來操作剛才生成的檔案了~