章 18. 文件上傳處理

目錄
POST 方法上傳
關於錯誤訊息的解釋
一些注意事項
上傳多個文件
對 PUT 方法的支援

POST 方法上傳

PHP 能夠接受任何來自符合 RFC-1867 標準的瀏覽器(包括 Netscape Navigator 3 及更高版本,Microsoft Internet Explorer 3 加微軟補丁,或者更高版本)上傳的文件。PHP 的這種特性使得我們既可以上傳文本文件,也可以上傳二進制文件。利用 PHP 的認証和文件操作函式,您就可以控制誰有上傳的權限,以及在文件上傳後進行哪些處理。

相關的設置: 請參閱 php.inifile_uploadsupload_max_filesizeupload_tmp_dir 以及 post_max_size 設置選項。

請注意 PHP 也支援 PUT 方法的文件上傳,Netscape Composer 和 W3C 的 Amaya 客戶端使用這種方法。請參閱 PUT 方法支援以獲取更多訊息。

您可以如下建立一個特殊的表單來支援文件上傳:

例子 18-1. 文件上傳表單

<form enctype="multipart/form-data" action="_URL_" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="1000">
Send this file: <input name="userfile" type="file">
<input type="submit" value="Send File">
</form>

該範例中的 _URL_ 應該指向一個 PHP 文件。 MAX_FILE_SIZE 隱藏字段必須在文件輸入字段之前,且其值為接受文件的最大尺寸,單位為字節。

警告

MAX_FILE_SIZE 的值只是對瀏覽器的一個建議,實際上它可以被簡單的繞過。因此不要把對瀏覽器的限制寄希望於該值。實際上,PHP 設置中的上傳文件最大值,是不會失效的。

為上傳文件定義的變數會根據 PHP 的版本及設置的不同而不同。自動全域變數 $_FILES 從 PHP 4.1.0 版本開始被支援。在這之前,從 4.0.0 版本開始,PHP 支援 $HTTP_POST_FILES 陣列。這些陣列將包含所有關於您上傳的文件的訊息,其中,我們推薦您使用 $_FILES。如果 PHP 的設置選項 register_globalson,則相關的變數名將也會存在。從 PHP 4.2.0 版本開始,register_globals 的預設值被設為 off

以上範例中 $_FILES 陣列的內容如下所示。我們假設文件上傳字段的名稱如上例所示,為 userfile

$_FILES['userfile']['name']

客戶端機器文件的原名稱。

$_FILES['userfile']['type']

文件的 MIME 型別,需要瀏覽器提供該訊息的支援,例如“image/gif”。

$_FILES['userfile']['size']

已上傳文件的大小,單位為字節。

$_FILES['userfile']['tmp_name']

文件被上傳後在伺服端儲存的臨時文件名。

$_FILES['userfile']['error']

和該文件上傳相關的錯誤代碼['error'] 是在 PHP 4.2.0 版本中增加的。

註: 在 PHP 4.1.0 版本以前該陣列的名稱為 $HTTP_POST_FILES,它並不像 $_FILES 一樣是自動全域變數。PHP 3 不支援 $HTTP_POST_FILES 陣列。

php.ini 中的 register_globals 被設置為 on 時,您可以使用更多的變數。例如,$userfile_name 等價於 $_FILES['userfile']['name']$userfile_type 等價於 $_FILES['userfile']['type'] 等。請記住從 PHP 4.2.0 開始,register_globals 的預設值為 off,因此我們建議您不要依賴於改設置項而使用剛剛提到的那些附加變數。

文件被上傳後,預定地會被儲存到伺服端的預定臨時目錄中,除非您將 php.ini 中的 upload_tmp_dir 設置為了其它的路徑。伺服端的預定臨時目錄可以通過更改 PHP 執行環境的環境變數 TMPDIR 來重新設置,但是在 PHP 腳本內部通過執行 putenv() 函式來設置是不起作用的。該環境變數也可以用來確認其它的操作也是在上傳的文件上進行的。

例子 18-2. 使文件上傳生效

以下例程適用於 PHP 4.0.2 以上版本。請參閱函式 is_uploaded_file()move_uploaded_file()

<?php 
// In PHP earlier then 4.1.0, $HTTP_POST_FILES  should be used instead of $_FILES.
if (is_uploaded_file($_FILES['userfile']['tmp_name'])) {
    copy($_FILES['userfile']['tmp_name'], "/place/to/put/uploaded/file");
} else {
    echo "Possible file upload attack. Filename: " . $_FILES['userfile']['name'];
}
/* ...or... */
move_uploaded_file($_FILES['userfile']['tmp_name'], "/place/to/put/uploaded/file");
?>

接受上傳文件的 PHP 腳本必須在文件上傳後進行判斷,來決定接下來要對該文件進行那些操作。例如,您可以通過 $_FILES['userfile']['size'] 變數來忽略尺寸太大或太小的文件,也可以通過 $_FILES['userfile']['type'] 變數來過濾文件型別和某種標準不相符合的文件。在 PHP 4.2.0 以上版本,您還可以通過 $_FILES['userfile']['error'] 變數來根據不同的錯誤代碼來做相關的判斷。不管做何種的判斷,您必須將該文件從臨時目錄中刪除,要麼將其移動到其它的地方。

如果該文件沒有被移動到其它地方也沒有被改名,則該文件將在表單請求結束時被刪除。