一、文件上传
为了让客户端的用户能够上传文件,我们必须在用户界面中提供一个表单用于提交上传文件的请求。由于上传的文件是一种特殊数据,不同于其它的post数据,所以我们必须给表单设置一个特殊的编码:
以上的enctype属性,你可能不太熟悉,因为这常常会被忽略掉。但是,如果http post请求中既有常规数据,又包含文件类数据的话,这个属性就应该显示加上,这样可以提高针对各种浏览器的兼容性。
接下来,我们得向表单中添加一个用于上传文件的字段:
上述文件字段在各种浏览器中可能表现会有所不同。对于大多数的浏览器,上述字段都会被渲染成一个文本框加上一个浏览按钮。这样,用户既可以自行输入文件的路径到文本框中,也可以通过浏览按钮从本地硬盘上选择所要上传的文件。但是,在苹果的Safari中,貌似只能使用浏览这种方式。当然,你也可以自定义这个上传框的样式,使它看起来比默认的样式优雅些。
下面,为了更好的阐述怎么样处理文件上传,举一个完整的例子。比如,以下一个表单允许用户向我的本地服务器上上传附件:
提示:可以通过php.ini中的upload_max_filesize来设置允许上传文件的最大值。另外,还有一个post_max_size也可以用来设置允许上传的最大表单数据,具体意思就是表单中各种数据之和,所以你也可以通过设置这个字段来控制上传文件的最大值。但是,注意后者的值必须大于前者,因为前者属于后者的一部分表单数据。
圖1. 顯示在firefox中的上傳表單
當這個表單提交的時候,http請求就會被送到upload.php。為了顯示具體哪些資訊可以在upload.php中使用,我在upload.php將其列印出來:
header('Content-Type: text/plain');
print_r($_FILES);
下面來做個試驗,假如我透過以上表單上傳一個本部落格的logo到我的本機伺服器www.360weboy.me/upload.php,看看在upload.php中會輸出什麼資訊:
Array
(
(
(
[name] => boy.jpg
[type] => image/jpeg
[tmp_name] => D:xampptmpphp1168.tmp
[error] => 0
[size] => 11490
)
)
以上就是檔案上傳後,在全域陣列中的所有關於目前上傳檔案的資訊。但是,我們是否能夠保證這些資訊是安全的,假如name或其它資訊被竄改過了呢?我們時時刻刻需要對來自客戶端的訊息保持警惕!
具體的http請求的各個部分
為了更好的理解文件上傳,我們必須核對下客戶端發送的http請求中到底包含了那些具體的信息。先前我上傳的附件是本部落格的logo,因為是圖片,不太適合我們做以上實驗。所以,我重新上傳一個test.text文字文件,其中具體包含了以下內容:
360w
360days
Life Of A Web Boy
Okay。現在我上傳這個文字文件,在upload.php中會輸出:
Array
(
[name] => test.txt
[ tmp_name] => D:xampptmpphp51C0.tmp
[且 40
)
下相關的瀏覽器發送的http post請求(一些可選的頭部我省略了):
複製程式碼
程式碼如下:
POST /upload.php HTTP/1.1
Host: www.360weboy.me Content-Length: 234
-----🎜>
-----🎜>
-----🎜>
------------------------24464570528145
Content-Disposition: form-data; name="attachment"; filename="test.txt"
Content-Type: text/plain
360weboy
------------- ----------------24464570528145--
從上面的請求格式中有幾個字段我們要關注下的,分別是name, filename以及Content-Type.它們分別表示上傳檔案框在form表單中的欄位名稱-attachment,使用者從本機硬碟上傳的檔案名稱– test.txt,以及上傳的檔案格式– text/plain(代表文字檔案)。然後,我們看到一行空行下面的,就是這個上傳檔案中的具體內容。
二、安全性的加強為了加強檔案上傳中的安全性,我們需要檢查下$_FILES全域數組中的tmp_name和size。為了確保tmp_name指向的文件確實是剛剛使用者在客戶端上傳的文件,而不是指向的類似/etc/passwd,可以使用php中的函數is_uploaded_file()來進行下判斷:
>
if (is_uploaded_file($filename)) {
情況下,用戶上傳文件後,可能會將上傳成功的檔案的內容顯示給使用者看下,那麼上述程式碼的檢查尤其重要。
另外一個要檢查的就是上傳檔案的mime-type, 也就是上述upload.php中輸出陣列的type欄位。 我在第一個例子中上傳的是一個圖片,所以$_FILES['attachment']['type']的值為'image/jpeg'。 如果打算在伺服器端只接受image/png, image/jpeg, image/gif, image/x-png 以及image/p-jpeg這些mime-type的圖片,可以用類似下面的程式碼了進行檢查(只是舉個例子,具體程式碼,例如報錯等,應該遵循你的系統中的機制):
複製程式碼
程式碼如下:
if(!in_array($image['type'], $ allow_mimes)) {
die('對不起, 你上傳的檔案格式不準確;我們只接受圖片檔案.');
如你所看到的,我們已經保準了檔案的mime-type是符合伺服器端的要求的。但是,這樣是不是就可以防止惡意使用者上傳其它有害文件,還是不夠的,因為這個mime-type惡意使用者是可以偽裝的。 例如使用者做了一張jpg圖片,在圖片的元資料中寫入了一些惡意的php程式碼,然後儲存為後綴名為php的檔案。當這個惡意檔案上傳的時候,將順利通過伺服器端對於mime-type的檢查,被認為是一張圖片,裡面的危險的php程式碼將會被執行。具體的圖片的元資料類似如下:
程式碼如下:
File size : 182007 bytes
Resolution : 1197 x 478
Comment : passthru($_POST['cmd ']); __halt_compiler();
複製程式碼
程式碼如下:
$allow_mimes = array(
'image/png' => '.png', 'image/ gif' => '.gif',
'image/jpeg' => '.jpg',
🎜>
$ image = $_FILES['attachment'];
if(!array_key_exists($image['type'], $allow_mimes )) {
你上傳準確的文件不證;我們只接受圖片檔案.');
}
// 取得略去後綴名的檔案名稱:
$image['name'], '.'));
// 上字尾名
$filename .= $allow_mimes[$image['type'];
透過上述的程式碼,我們確保即使上傳的圖片的元檔案包含了php程式碼的話,圖片檔案會被重命名為後綴為圖片格式的文件,所以其中的php程式碼也不會被執行了。上述程式碼對正常的上傳的圖片也不會有任何負面影響。
進行了上述的幾步提高安全性的檢查步驟後,如果你只是要把上傳的檔案保存到一個指定的目錄中,那麼就可以使用php的預設函數move_uploaded_file來實現了:
代碼如下:
'];
$filename = '/path/to/attachment.txt';
_filename 保存在暫存目錄中的上傳檔案, 然後成功將其儲存到對應目錄下的attachment.txt檔案中. */
}
你也許還要對上傳檔案的大小進行限制,那麼你可以透過filesize函數來取得上傳檔案的大小,進行判斷後做進一步處理,這具體就不在這將了,自己去折騰吧。
好了,關於文件上傳暫時就寫到這裡吧。希望這篇入門文章對你有幫助。