|
1、 全局變量$_FILES[『xxx』];
PHP5新創了一個$_$_FILES全局變量來接收文件的上傳。這和以往版本有些差別。在舊版本裡,你可以打開register_globals=on直接使用全局變量,或使用$HTTP_POST_FILSE[『xxx』],這裡只研究PHP5
2、文件上傳表單
1,上傳文件的表單使用post方式(和get的區別不用說了);還要加上enctype=』multipart/form-data』。
2,一般要加上隱藏域:
<input type=hidden name=』MAX_FILE_SIZE』 value=」30000」>,位置在file域前面。value的值是上傳文件的客戶端字節限制。據說可以減少文件超標時客戶端的等待時間,不過我沒覺得有什麼區別。
注意:MAX_FILE_SIZE 的值只是對瀏覽器的一個建議,實際上它可以被簡單的繞過。因此不要把對瀏覽器的限制寄希望於該值。實際上,PHP.ini 設置中的上傳文件最大值,是不會失效的。但是最好還是在表單中加上 MAX_FILE_SIZE,因為它可以避免用戶在花時間等待上傳大文件之後才發現該文件太大了的麻煩。
3,出於安全考慮,file域是不許賦值的。隨便在file域輸入字符串,然後按submit也不會有反應。必須是第二個字符是冒號的時候(比如空格跟隨冒號可以上傳一個長度為0字節的「文件」),submit才同意「服務」——不過這個是客戶端的措施,跟MAX_FILE_SIZE一樣很容易繞過去。
Page1.php:
<form enctype=」multipart/form-data」 action=」page2.php」 method=」POST」>
<input type=」hidden」 name=」MAX_FILE_SIZE」 value=」8388608〞>
上傳文件: <input name=」userfile」 type=」file」>
<input type=」submit」 value=」提交」>
</form>
3、上傳8M以下文件如何設置php.ini;
打開php.ini,首先找到
File Uploads區域,
有影響文件上傳的以下幾個參數:
file_uploads = on ;是否允許通過HTTP上傳文件的開關。默認為ON即是開
upload_tmp_dir ;文件上傳至服務器上存儲臨時文件的地方,如果沒指定就會用系統默認的臨時文件夾
upload_max_filesize = 8388608 ;望文生意,即允許上傳文件大小的最大值。默認為2M,必須換算成byte單位:1M=1024K;1K=1024byte
Data Handling ;區域,
還有一項:post_max_size = 8m ;指通過表單POST給PHP的所能接收的最大值,包括表單裡的所有值。默認為8M
一般地,設置好上述四個參數後,在網絡正常的情況下,上傳<=8M的文件是不成問題。
4、 上傳大於8M文件如何設置php.ini;
如果要上傳>8M的大體積文件,只設置上述四項還一定能行的通。除非你的網絡真有100M/S的上傳高速,否則你還得關心關心下面的參數:
Resource Limits
max_execution_time = 600 ;每個PHP頁面運行的最大時間值(秒),默認30秒
max_input_time = 600 ;每個PHP頁面接收數據所需的最大時間,默認60秒
memory_limit = 8m ;每個PHP頁面所吃掉的最大內存,默認8M
把上述參數修改後,在網絡所允許的正常情況下,就可以上傳大體積文件了。
好了,設置好現在就可以一試。點擊一個200大M的文件上傳一下,在你聽歌、想MM或上廁所回來過程中,程序會告訴你上傳成功啦~在本機上測試上傳200M的文件成功。
另外,據說上傳大文件最好用FTP方法,我對PHP的FTP不太熟,這裡就不研究了。
5、 文件上傳錯誤代碼
預定義變量$_FILES數組有5個內容:
$_FILES[』userfile』][』name』]——客戶端機器文件的原名稱
$_FILES[』userfile』][』type』]——文件的 MIME 類型
$_FILES[』userfile』][’size』]——已上傳文件的大小,單位為字節
$_FILES[』userfile』][』tmp_name』]——文件被上傳後在服務端儲存的臨時文件名
$_FILES[』userfile』][』error』]——和該文件上傳相關的錯誤代碼
其中$_FILES[』userfile』][』error』]的可以有下列取值和意義:
0——沒有錯誤發生,文件上傳成功。
1——上傳的文件超過了 php.ini 中 upload_max_filesize 選項限制的值。
2——上傳文件的大小超過了 HTML 表單中 MAX_FILE_SIZE 選項指定的值。
3——文件只有部分被上傳。
4——沒有文件被上傳。
1~3不用說了。「沒有文件被上傳」(4)是指表單的file域沒有內容,是空字符串。
「文件上傳成功」(0)不一定真的有文件上傳了。比如你打了個「c:」給file域,就可以「上傳成功」——錯誤代碼是0,[』name』]是「c:」,[』type』]是「application/octet-stream」,[’size』]是0,[』tmp_name』]是「xxx.tmp」(xxx是服務器起的名字)
這裡我有個小發現,起初使用PHP5.14時,無論page1.php裡面<input name=」userfile」
type=」file」>的name等於什麼,在上傳處理腳本中都只能用$_FILES[』userfile』]來接收;
後來改用PHP5.16,發現$_FILES[』userfile』]必須和<input name=」userfile」 type=」file」>命名相對應了。比如name=」book」,則處理腳本必須用$_FILES[』book』]來接收。請大家注意一下自己的版本號。
6、上傳文件大小限制
限制上傳文件大小的因素有
1,客戶端限制:隱藏域MAX_FILE_SIZE的數值(可以被繞開)。
2,服務器端限制:upload_max_filesize,post_max_size和memory_limit。這幾項不能夠用腳本來設置,也可以在php.ini裡設置,參考本文3、4條。
3,自定義限制。
7、上傳文件格式檢驗
1、檢驗文件格式的方法應該很多,可以檢測MIME類型,也可以用正則表達式判斷,這裡推
薦一種我常用的:
//自定義允許的文件上傳格式;
$upload_type=」.jpeg,.png,.gif,.rar,.zip,.chm,.html,.htm」;
//檢驗上傳文件是否符合格式的函數。
function check_type($upload_type){
$file_name=$_FILES[』$form』][』name』];
$findtype=strtolower(strrchr($file_name,」.」));
$allow=strpos($upload_type,$findtype);
if($allow===false){
echo」文件格式不符」;
exit;
}
}
2、 關於通過$_FILES[』userfile』][』type』]——文件MIME類型來檢查上傳格式,有必要對MIME進行一些討論。
什麼是MIME?(不必深入瞭解,粗知大略即可)
MIME表示多用途Internet郵件擴允協議。MIME擴允了基本的面向文本的Internet郵件系統,以便可以在消息中包含二進制附件
RFC822在消息體的內容中做了一點限制:就是只能使用簡單的ASCII文本。所以,MIME信息由正常的Internet文本郵件組成,文本郵件擁有一些特別的符合RFC822的信息頭和格式化過的信息體(用ASCII的子集來表示的附件)。這些MIME頭給出了一種在郵件中表示附件的特別的方法。
MIME信息包含了哪些東西?
一個普通的文本郵件的信息包含一個頭部分(To: From: Subject: 等等)和一個體部分(Hello Mr.,等等)。在一個符合MIME的信息中,郵件的各個部分叫做MIME段,每段前也綴以一個特別的頭。MIME郵件只是基於RFC 822郵件的一個擴展。然而它有著自已的RFC規範集。
頭字段
MIME頭根據在郵件包中的位置,大體上分為MIME信息頭和MIME段頭,MIME信息頭指整個郵件的頭,而MIME段頭只每個MIME段的頭。
MIME信息頭有:
MIME-Version:
這個頭提供了所用MIME的版本號。這個值習慣上為1.0。
Content-Type:
它定義了數據的類型,以便數據能被適當的處理。有效的類型有:text,image, audio,video,applications,multipart和message。注意任何一個二進制附件都應該被叫做application/octet-stream。這個頭的一些用例為:image/jpg, application/mswork,multipart/mixed 。
Content-Transfer-Encoding:
它說明了對數據所執行的編碼方式,客戶/MUA將用它對附件進行解碼。對於每個附件,可以使用7bit,8bit,binary ,quoted-printable,base64和custom中的一種編碼方式。7bit編碼是用在US ASCII字符集上的常用的一種編碼方式。8bit 和binary編碼一般不用。對可讀的標準文本,如果傳輸要經過對格式有影響的網關時對其進行保護,可以使用quoted printable 。Base64是一種通用方法,在需要決定使用哪一種編碼方法時,它提供了一個不用費腦子的選擇;它通常用在二進制,非文本數據上。注意,任何非7bit 數據必須用一種模式編碼,這樣它就可以通過Internet郵件網關。
Content-ID:
如果Content-Type是message/external-body或multipart/alternative時,這個頭就有用了。
Content-Descrīption:
這是一個可選的頭。它是任何信息段內容的自由文本描述。描述必須使用us-ascii碼。
Content-Disposition:
這是一個試驗性的頭,它用於給客戶程序/MUA提供提示,來決定是否在行內顯示附件或作為單獨的附件。
當然,在使用PHP檢查上傳格式時,沒有必要把MIME搞得那麼複雜,對那些高深的理論,粗知大略就可以了。倘若我們要上傳ZIP和RAR的文件,你可以在接收文件的php中寫上:
$filetype=$_FILES[』userfile』][』type』];
echo」$filetype」;
就可看到上傳文件的mime類型.
ZIP的MIME類型用application/x-zip-compressed
RAR的MIME類型用application/octet-stream
然後再用如下代碼判斷格式:
switch ($_FILES[』userfile』][』type』]) {
case 「application/x-zip-compressed」:
break;
case 「application/octet-stream」:
break;
case 「text/plain」:
break;
default:
print 「您上傳的文件類型不對」;
exit(0);
break;
}
個人認為上傳文件最好的辦法使用一個功能齊全的類,自己寫的話太麻煩了。PHP的競爭對手ASP.NET都把文件上傳,廣告輪顯等功能做成組件了,可視化操作,瞧得讓人眼熱——當然,眼熱歸眼熱,我還是會極力克制自己不去學的。理由很簡單,無論是ASP.NET,PHP還是JSP,其作用都是做網站。既然作用都一樣,那麼好比殺豬,用剔骨尖刀是殺豬,用太極劍法是殺豬,即使你聰明英俊才華蓋世玉樹臨風,精通太極劍,剔骨刀,宰牛刀三種武藝來殺豬,其結果也只有一個——豬被殺死了;那麼,我寧肯只學一刀,只精通一刀,我也能把豬殺的很不錯。有那麼多時間、那麼多精力去重複學習一些作用相同的技術,倒不如學點別的,比如為人處事,經營管理,或者琢磨一下如何追MM,以解決程序員找對象老大難的問題等。
PHP對組件的支持非常不好,這是它的一個缺點。但傳說Zend公司許諾明年將發佈Zendbox,大力支持組件,也不知效果如何。Zend今年給我們太多的承諾了,說明年這也發佈那也發佈……強力期盼中……
8、開始上傳文件
//上接page1.php,這裡為了說明原理,省略了對上傳文件格式、大小的判斷。
Page2.php
<?php
// 在 4.1.0 以前的 PHP 中,需要用 $HTTP_POST_FILES 代替 $_FILES。
// 在 4.0.3 以前的 PHP 中,需要用 copy()來代替 move_uploaded_file()。
//上傳文件的路徑,這裡是基於linux的,如有需要請自行修改。
$uploaddir = 『/var/www/uploads/』;
$uploadfile = $uploaddir. $_FILES[』userfile』][』name』];
print 「<pre>」;
if (is_uploaded_file($_FILES[』userfile』])) {
if (move_uploaded_file($_FILES[』userfile』][』tmp_name』], $uploadfile)) {
print 「文件上傳成功:\n」;
print_r($_FILES);
} else {
print 「文件上傳失敗:\n」;
print_r($_FILES);
}
}
print 「</pre>」;
?>
is_uploaded_file():該函數用來判斷文件到底是客戶端上傳的呢還是本機原有的。傳說中國外有個軟件,它可以迷惑服務器,並使其誤把本機文件當成是客戶端上傳的。這就很危險了:如果在linux服務器下,不法分子把包含root密碼的敏感文件竊而走之,你的主機就馬上變肉雞,任人宰割了。
move_uploaded_file()把客戶端上傳的文件從臨時文件裡移走。和copy相比,該函數顯得安全多了。它只移走客戶端上傳的文件;至於移動本機文件,還是用copy吧.
9、下面演示一個簡單的文件上傳例子(20M—25M):
Php.ini設定:
post_max_size=23068672
file_uploads = On
upload_max_filesize=26214400
max_execution_time = 600
max_input_time = 600
memory_limit = 10M
設定完記得重啟apache或IIS。
Page1.php:
<form enctype=」multipart/form-data」 action=」page2.php」 method=」POST」>
<input type=」hidden」 name=」MAX_FILE_SIZE」 value=」8388608〞>
上傳文件: <input name=」myup」 type=」file」>
<input type=」submit」 value=」提交」>
</form>
Page2.php
<html>
<head>
<title>文件上傳</title>
</head>
<body>
9、下面演示一個簡單的文件上傳例子(20M—25M):
Php.ini設定:
post_max_size=23068672
file_uploads = On
upload_max_filesize=26214400
max_execution_time = 600
max_input_time = 600
memory_limit = 10M
設定完記得重啟apache或IIS。
Page1.php:
<form enctype=」multipart/form-data」 action=」page2.php」 method=」POST」>
<input type=」hidden」 name=」MAX_FILE_SIZE」 value=」8388608〞>
上傳文件: <input name=」myup」 type=」file」>
<input type=」submit」 value=」提交」>
</form>
Page2.php
<html>
<head>
<title>文件上傳</title>
</head>
<body>
<h1>文件上傳……</h1>
<?php
//判斷上傳錯誤;
if($_FILES[』myup』][』error』]>0){
echo」上傳失敗:」;
switch($_FILES[』myup』][』error』]){
//文件上傳錯誤代碼,具體原因參照本文第5條;
case 1: echo」上傳失敗」;break;
case 2: echo」上傳失敗」;break;
case 3: echo」上傳失敗」;break;
case 4: echo」上傳失敗」;break;
}
}
/**********************************************************
記得在httpd.conf所指定的DocumentRoot目錄外新建一個upfile文件夾,
將之用來保存上傳的文件。之所以要把upfile放在DocumentRoot目錄外,是為了
防止客戶端上傳一些別有用心的PHP代碼來破壞站點,因為在DocumentRoot目錄外的
PHP代碼是無法執行的。在linux系統下,需確保upfile的權限為777。
***********************************************************/
$upfile=』../upfile/』.$_FILES[』myup』][』name』];
if(is_uploaded_file($_FILES[』myup』][』tmp_name』])){
if(!move_uploaded_file($_FILES[』myup』][』tmp_name』],$upfile)){
echo 「文件上傳失敗」;
exit;
}else{
echo 「文件上傳成功!」;
}
}
?>
</body>
</html>
10、一個簡單的文件上傳類:
這個類很簡單的,功能並不齊全;我另有一個功能強大的寶貝類,獨家收藏,絕不外傳…….呵呵,其實也是從phpchina找到的。本人學習文件上傳的方法就是:在phpchina搜索相關的帖子,全部保存下來,然後潛行研究,務求精熟,這樣你會發現很多書本上沒有的知識,如果不嫌我的辦法笨的話,phper朋友們可以試試。
class upload{
public $formname=」userfile」;//表單元素file的名字;
public $upload_path=」upload/」;//上傳文件的保存路徑;
public $upload_file_size=」10485760〞;//允許上傳文件的大小;
public $upload_type=」.jpeg,.png,.gif,.rar,.zip,.chm,.html,.htm」;
function __construct($formname1=」userfile」,$upload_file_size1=」10485760〞,$upload_path1=」upload/」,$upload_type1=」.jpeg,.png,.gif,.rar,.zip,.chm,.html,.htm」){
$this->upload_false();
}
//少數瀏覽器支持PUT方法而不支持POST,可以據check_method()函數檢驗;
function check_method(){
if($_SERVER[』REQUEST_METHOD』]!=』POST』){
echo」對不起,您的瀏覽器不支持POST方法上傳,如要繼續使用本站,請使用IE或Firefox瀏覽器!」;
exit;
}
}
//檢查文件格式是否符合規定;
function check_type($upload_type1){
$form=$this->formname;
$file_name=$_FILES[』$form』][』name』];
$findtype=strtolower(strrchr($file_name,」.」));
$allow=strpos($upload_type,$findtype);
if($allow===false){
echo」文件格式不符」;
exit;
}
}
//檢驗是否從本地上傳文件;
function startup(){
if(!is_uploaded_file($_FILES[$this->formname][』name』])){
echo 「不允許從服務器端向服務器端上傳文件!」;
exit;
}
$path=$this->upload_path;
$filename=$path.」/」.$_FILES[$this->formname][』name』];
if(!move_uploaded_file($_FILES[$this->formname][』tmp_name』],$filename)){
echo 「文件無法移動,上傳失敗!」;
exit;
}
echo 「文件上傳成功!」;
}
function upload_false(){
$the_error=$_FILES[$this->formname][』error』];
switch ($the_error){
case 0:echo 「文件上傳成功!」;break;
case 1:echo 「上傳文件的大小超出規定!」;break;
case 2:echo 「上傳文件的大小超過了MAX_FILE_SIZE的指定值!」;break;
case 3:echo 「文件只有部分被上傳!」;break;
case 4:echo 「文件沒有被上傳!」;break;
}
$this->startup();
$this->check_type();
$this->check_method();
}
}
Reference: http://www.juuyou.com/index.php?tag=upload |
|