PHP開發基礎教學之文件上傳LOGIN

PHP開發基礎教學之文件上傳

一、PHP檔案上傳

在我們日常使用中常常會遇到很多種這樣的情況:

  • #QQ空間裡面上傳圖片呀

  • 微信朋友圈上傳圖片

  • 寄郵件裡面上傳郵件資料附件

  • 認證的時候要求上傳照片或身分證

  •  還有各種產品汪(gou)們提出的需求來分析,上傳不同的東西。

  • 而產品汪提出來的需求我們需要實現。

  • 實作檔案上傳,是PHP程式設計師必備的技能之一。

透過學習檔案上傳,你將透過使用的現像看到檔案上傳的本質!


二、檔案上傳需要注意php.ini檔案

在正式解說本章上傳前,我們第一件事情是要注意觀察php.ini檔案。

我們第一次開始接觸到如何修改php.ini文件,如果你的設定項與我們說的不一致,請注意修改。

我們來了解每一個組態項目。

我們來看看如何修改php.ini。

開啟phpstudy中的php.ini設定檔。

 52.png

php.ini的檔案太多,找不到的時候可以用ctrl+f 搜尋相關設定項。


53.png  


建議尺寸: file_size(檔案大小) < upload_max_filesize < post_max_size < memory_limit

另外,需要注意的是腳本執行時間。

max_execution_time,這什參數的單位為秒。
這個參數是設定腳本的最大執行時間。
也可以根據需求做適當的改變。通常不需要來修改,系統預設值即可。超大檔案上傳的時候,可能會牽涉到這項參數的修改。

上傳時間太長了,會逾時。如果你將此項參數設為0,則是不限制逾時時間,不建議使。

完成了php.ini的相關配置,我們就可以開始試著完成第一次檔案上傳了。


三、檔案上傳的步驟

#為了更好的學習PHP,我們將極為複雜的PHP的檔案上傳歸納總結成為了6個步驟。

在實際使用過程中,你依照這6個步驟就能夠很好的完成PHP的檔案上傳:

1.判斷是否有錯誤碼

#系統傳回的錯誤碼詳解:


54.png


#註:錯誤碼中沒有5

2.自訂判斷是否超出檔案大小範圍

在開發上傳功能時。我們作為開發人員,除了php.ini中規定的上傳的最大值外。

我們通常也會設定一個值,是業務規定的上傳大小限制。

例如:
新浪微博或QQ空間只準單張頭像圖片2M。而在上傳圖冊的時候又可以超過2M來上傳。

所以說,它的系統是支援更大檔案上傳的。

此處的判斷檔案大小,我們用來限制實際業務中我們想要規定的上傳的檔案大小。

3.判斷後綴名和mime型別是否符合

在網路世界裡面也有壞人。他們會把圖片插入病毒,在附件中上傳病毒,他們會在網頁中插入病毒或黃色圖片。

我們需要對於上傳的檔案後綴和mime類型都要進行判斷才可以。

MIME(Multipurpose Internet Mail Extensions)是多用途互聯網郵件擴充類型。是設定某種副檔名的檔案用一種應用程式來開啟的方式類型,當該副檔名檔案被存取的時候,瀏覽器會自動使用指定應用程式來開啟。多用於指定一些客戶端自訂的檔案名,以及一些媒體檔案開啟方式。

在判斷後綴和MIME類型的時候,我們會用到PHP的一個函數in_array(),該函數傳入兩個參數。
第一個參數是要判斷的值;
第二個參數是範圍陣列。

我們用這個函數來判斷檔案的後綴名稱和mime類型是否在允許的範圍內。

4.產生檔案名稱

我們的檔案上傳成功了,不會讓它儲存原名。
因為,有些人在原名中有敏感關鍵字會違反我國的相關法律和法規。

我們可以採用date()、mt_rand()或unique()產生隨機的檔案名稱。

 

5.判斷是否為上傳檔案

#檔案上傳成功時,系統會將上傳的暫存檔案上傳到系統的暫存目錄中。產生一個臨時文件。

同時會產生暫存檔案名稱。我們需要做的事情是將臨時檔案移到系統的指定目錄中。

而移動前不能瞎移動,或是移動錯了都是不科學的。移動前我們需要使用相關函數判斷上傳的檔案是不是暫存檔案。

is_uploaded_file()傳入一個參數($_FILES中的快取檔案名稱),判斷傳入的名稱是不是上傳檔案

6.移動暫存檔案到指定位置

臨時文件是真實的臨時文件,我們需要將其移動到我們的網站目錄下面了。

讓我們網站目錄的數據,其他人可以存取。

我們使用:move_uploaded_file()。
這個函數是將上傳檔案移到指定位置,並命名。
傳入兩個參數:
第一個參數是指定移動的上傳檔案;
第二個參數是指定的資料夾和名稱拼接的字串


四、檔案上傳表單注意事項

#

我們開始正式的學習,學習如何來上傳檔案。上傳文件必須在網頁中準備好一個form表單。

這是一個簡單的HTML頁面表單,form表單為檔案內容準備了一個專用的類,當input 裡面 的 type=file 時,預設為上傳檔案內容。

我們來看看表單的程式碼和注意項目

<html>
    <head>
        <meta charset="utf-8" />
        <title>单文件上传</title>
    </head>
    <body>
        <form action="file.php" method="post" enctype="multipart/form-data">
            <input type="file" name="file">
            <input type="submit" value="上传">
        </form>
    </body>
</html>

註:

  • form 表單中的參數method 必須為post。若為get是無法進行檔案上傳的

  •  enctype必須為multipart/form-data



55.png

##五、依照陣列和步驟完成檔案上傳

1.系統函數$_FILES

form表單提交的檔案內容指向了file.php 。

我們在file.php中,透過PHP程式碼,來處理上傳檔案。

我們選擇一個名為圖片進行上傳。假設圖片的名字為:hua.jpg,點選上傳。 PHP為檔案類別資料準備了一個專用的系統函數$_FILES,上傳檔案的所有相關數據,都保存在這個系統函數中。

在PHP檔案中,我們印出$_FILES ,來觀察這個陣列的結構:

<?php
//var_dump()或print_r()
//打印变量的相关信息,将变量的信息详细的展示出来
var_dump($_FILES);             
?>

列印出來的結果的陣列結構如下:

 

#得到了上面的陣列結構。

我們就可以開始文件的處理過程了。

2.判斷錯誤碼

<?php
if($_FILES['file']['error'] > 0){
    switch ($_FILES['file']['error']) {    //错误码不为0,即文件上传过程中出现了错误
        case '1':
            echo '文件过大';
            break;
        case '2':
            echo '文件超出指定大小';
            break;
        case '3':
            echo '只有部分文件被上传';
            break;
        case '4':
            echo '文件没有被上传';
            break;
        case '6':
            echo '找不到指定文件夹';
            break;
        case '7':
            echo '文件写入失败';
            break;
        default:
            echo "上传出错<br/>";
    }
}else{
    //错误码为0,即上传成功,可以进行后续处理,处理流程见下文
}
?>
上面的程式碼詳細的介紹了錯誤碼和對應的錯誤,我們可以根據錯誤碼,來產生準確的錯誤提示。

######3.判斷檔案是否超出大小#########在實際專案中,由於系統硬體的限制,以及儲存裝置的限制,不可能讓使用者無限的上傳文件,所以我們要對使用者上傳的文件大小進行限制。定義一個適當的限制大小,能讓我們的應用更穩定的運作。 ###
//判断错误
if($_FILES['file']['error'] > 0){
        //有错误可停止执行
}else{
    //当前上传文件无误,运行本段代码
 
    //判断文件是否超出了指定的大小
    //单位为byte
    $MAX_FILE_SIZE = 100000;
    if($_FILES['file']['size'] > $MAX_FILE_SIZE){
    //判断,如果上传的文件,大小超出了我们给的限制范围,退上传并产生错误提示
        exit("文件超出指定大小");                           
    }
###將我們指定的檔案大小,定義為$MAX_FILE_SIZE,變數的計數單位為byte,對應上傳檔案的 $_FILES['file']['size']大小。 ###範例程式碼中,限制大小約為100K及以下的檔案。 #########4.判斷文件的mime類型是否正確#########更多的時候,我們的文件上傳功能,都需要判斷用戶上傳的文件,是否符合要求,不可用的文件上傳以後,對於線上應用的整體展示效果,會造成惡劣的影響。所以我們需要透過mime類型和後綴名,來判斷用戶上傳的檔案是否符合要求。 ######下面的範例程式碼中,我們假設當前的專案需求為指定上傳圖片,要求上傳後綴名為GIF或jpg的文件,當使用者上傳不符合要求的檔案時,會傳回錯誤提示。 ###
/*判断后缀名和MIME类型是否符合指定需求
例如:
当前项目指定上传后缀为.jpg或.gif的图片,则$allowSuffix = array('jpg','gif');
*/
//定义允许的后缀名数组
$myImg = explode('.', $_FILES['file']['name']);         
 
/*
explode() 将一个字符串用指定的字符切割,并返回一个数组,这里我们将文件名用'.''切割,结果存在$myImg中,文件的后缀名即为数组的最后一个值
*/
$myImgSuffix = array_pop($myImg);
/*
根据上传文件名获取文件的后缀名
使用in_array()函数,判断上传文件是否符合要求
当文件后缀名不在我们允许的范围内时退出上传并返回错误信息
*/ 
if(!in_array($myImgSuffix, $allowSuffix)){                             
        exit("文件后缀名不符");
}
/*
mime类型和文件后缀名的对应关系,我们可以通过很多途径查询到,为了避免用户自主修改文件后缀名造成文件无法使用。
mime类型也必须做出限制检查mime类型,是为了防止上传者直接修改文件后缀名
导致文件不可用或上传的文件不符合要求。
*/
//数组内容为允许上传的mime类型
$allowMime = array(
            "image/jpg",
            "image/jpeg",
            "image/pjpeg",
            "image/gif"
            );
if(!in_array($_FILES['file']['type'], $allowMime)){                      //判断上传文件的mime类型是否在允许的范围内
      exit('文件格式不正确,请检查');
    //如果不在允许范围内,退出上传并返回错误信息
}
######5.產生指定的路徑和檔案名稱######

依照專案的檔案安排,產生檔案儲存路徑,為了避免檔案名稱重複而產生的錯誤,依照一定的格式,產生一個隨機檔案名稱。

//指定上传文件夹
$path = "upload/images/";
/*
根据当前时间生成随机文件名,本行代码是使用当前时间 + 随机一个0-9的数字组合成文件名,后缀即为前面取到的文件后缀名
*/
$name = date('Y').date('m').date("d").date('H').date('i').date('s').rand(0,9).'.'.$myImgSuffix;

6.判斷是否為上傳檔案

is_uploaded_file()函數是專用的函數,來判斷目標檔案是否是上傳檔案。

<?php
//使用is_uploaded_file()判断是否是上传文件,函数介绍见上文
if(is_uploaded_file($_FILEs['file']['tmp_name'])){    
}
?>

7.移動檔案到指定位置

使用move_uploaded_file()函數,將檔案移到指定的位置,並命名。要注意的是,Linux系統中對目標目錄是否有權限及磁碟空間是否足夠,否則會導致上傳操作失敗。

/*
使用move_uploaded_file()移动上传文件至指定位置,第一个参数为上传文件,第二个参数为我们在前面指定的上传路径和名称。
*/
 
if(move_uploaded_file($_FILEs['file']['tmp_name'], $path.$name)){
            //提示文件上传成功
            echo "上传成功";                                
        }else{
/*
文件移动失败,检查磁盘是否有足够的空间,或者linux类系统中文件夹是否有足够的操作权限
*/
            echo '上传失败';                                                
        }
    }else{
        echo '不是上传文件';
    }
}
?>

完整程式碼如下:

<?php
if ($_FILES['file']['error'] > 0) {
    switch ($_FILES['file']['error']) {
        //错误码不为0,即文件上传过程中出现了错误
        case '1':
            echo '文件过大';
            break;
        case '2':
            echo '文件超出指定大小';
            break;
        case '3':
            echo '只有部分文件被上传';
            break;
        case '4':
            echo '文件没有被上传';
            break;
        case '6':
            echo '找不到指定文件夹';
            break;
        case '7':
            echo '文件写入失败';
            break;
        default:
            echo "上传出错<br/>";
    }
} else {

    $MAX_FILE_SIZE = 100000;
    if ($_FILES['file']['size'] > $MAX_FILE_SIZE) {
        exit("文件超出指定大小");

    }

    $allowSuffix = array(
        'jpg',
        'gif',
    );

    $myImg = explode('.', $_FILES['file']['name']);

    $myImgSuffix = array_pop($myImg);

    if (!in_array($myImgSuffix, $allowSuffix)) {
        exit("文件后缀名不符");
    }

    $allowMime = array(
        "image/jpg",
        "image/jpeg",
        "image/pjpeg",
        "image/gif",
    );

    if (!in_array($_FILES['file']['type'], $allowMime)) {
        exit('文件格式不正确,请检查');
    }

    $path = "upload/images/";
    $name = date('Y') . date('m') . date("d") . date('H') . date('i') . date('s') . rand(0, 9) . '.' . $myImgSuffix;

    if (is_uploaded_file($_FILEs['file']['tmp_name'])) {

        if (move_uploaded_file($_FILEs['file']['tmp_name'], $path . $name)) {
            echo "上传成功";
        } else {
            echo '上传失败';
        }

    } else {
        echo '不是上传文件';
    }

}
?>

六、多檔案上傳

#介紹了PHP上傳單一檔案的過程。但有些時候,為了使用方便,我們需要滿足同時上傳多個檔案的需求。多文件上傳原理相同,不過在處理資料時,需要對上傳資料進行特殊處理。

<html>
    <head>
        <meta charset="utf-8" />
        <title>单文件上传</title>
    </head>
    <body>
        <form action="morefile.php" method="post" enctype="multipart/form-data">
         <input type="file" name="file[]">
         <input type="file" name="file[]">
         <input type="submit" value="上传">
     </form>
    </body>
</html>

這裡有一個簡易的上傳頁面,form表單同時提交了兩個檔案。我們 可以透過這個頁面提交內容。

註:

  •  input type="file" name="file[]"與之前比較file後多加了一個中括號

  • 寫了2個或多個input type="file" name="file[]" 
    我們使用$_FILES 來接收檔案訊息,列印並查看陣列:

<?php
var_dump($_FILES); //打印$_FILES查看数组结构
?>

陣列結構如右所示

我們可以看到,兩個檔案被儲存在一個陣列中,鍵名和上傳單檔是相同。所以,需要我們用for()循環,來分別取出兩個檔案的需要用到的資料。

在$_FILES中同時保存了兩個檔案的數據,我們需要使用一個簡單的循環,來讀取單一檔案的信息,並將檔案移到我們想要放的位置。

for ($i=0; $i < count($_FILE['file']['name']); $i++) {  
/*
用is_uploaded_file()函数判断是上传文件
并且没有出现错
*/
    if(is_uploaded_file($_FILEs['file']['tmp_name'][$i]) && $_FILEs['file']['error'][$i] == 0){     
        if(move_uploaded_file($_FILEs['file']['tmp_name'][$i],'upload/'.$_FILE['file']['name'][$i])){
    //用move_uploaded_file()函数移动文件到指定的位置并使用文件原名
    echo "上传成功";
        }else{
            echo '上传失败';
        }
    }else{
       echo '上传失败';
    }
}

詳細的判斷過程,請參考單一檔案上傳,這裡只做了基本的判斷,並沒有對檔案的大小及格式提醒。 
請依照業務,自行判斷檔案大小及格式進行錯誤提醒。


七、檔案上傳進度處理

#當檔案過大,或是使用者網路狀態一般,通常上傳過程需要一段時間,如果這時候讓用戶白屏等待的話,相信大部分用戶都會直接關掉應用,所以一個監控上傳進度,並實時向用戶報告的需求就被產品汪擺在了桌面上。一個高逼格的上傳進度提示,會讓你的應用程式瞬間變成被仰望的存在。
PHP在5.4之前,總是需要安裝額外的擴充功能才能監控到檔案上傳進度。而從5.4開始,引入session.upload_progress的新特性,我們只需要在php.ini開啟配置,即可透過session監控檔案上傳進度。在php.ini中。

註:本章學習需要有session基礎和javascript和ajax基礎。

我們需要配置,注意查看和修改php.ini檔案:

56.png

開啟了配置,我們可以透過session,來記錄一個完整的檔案上傳進度。在session中,會出現一個如下結果的陣列:

 $_SESSION["upload_progress_test"] = array(
    //请求时间
     "start_time" => 1234567890,
     // 上传文件总大小
     "content_length" => 57343257,
     //已经处理的大小
     "bytes_processed" => 453489,
     //当所有上传处理完成后为TRUE,未完成为false
     "done" => false,
 
     "files" => array(
      0 => array(
        //表单中上传框的名字
 
       "field_name" => "file1",
 
       //上传文件的名称
       "name" => "test1.avi",
 
       //缓存文件,上传的文件即保存在这里
       "tmp_name" => "/tmp/phpxxxxxx",
 
       //文件上传的错误信息
       "error" => 0,
 
       //是否上传完成,当这个文件处理完成后会变成TRUE
       "done" => true,
 
       //这个文件开始处理时间
       "start_time" => 1234567890,
 
       //这个文件已经处理的大小
       "bytes_processed" => 57343250,     
      ),
 
      1 => array(
       "field_name" => "file2",
       "name" => "test2.avi",
       "tmp_name" => NULL,
       "error" => 0,
       "done" => false,                    
       "start_time" => 1234567899,
       "bytes_processed" => 54554,
      ),
     )
    );

這個陣列詳細記錄了檔案上傳的進度,已經處理完的檔案狀態為true。下面,我們透過一個jQuery的AJAX實例,來學習一下檔案上傳進度的流程。

首先,在表單中,需要新增一個type=hidden 的input 標籤,標籤value 為自訂(建議使用有一定意義的值,因為這個值將會在背景使用到)

<form id="upload-form" action="upload.php" method="POST" enctype="multipart/form-data" style="margin:15px 0" target="hidden_iframe">
    <input type="hidden" name="<?php echo ini_get("session.upload_progress.name"); ?>" value="test" />
    <p><input type="file" name="file1" /></p>
    <p><input type="submit" value="Upload" /></p>
</form>
<div id="progress" style="margin-bottom:15px;display:none;">
    <div>0%</div>
</div>

這裡,新增了一個ID為progress的div,作為展示上傳進度的容器。我們透過js的setTimeout(),定時執行ajax來取得檔案上傳進度,後台檔案傳回檔案上傳的進度百分比。

<script src="../jquery/1.8.2/jquery.min.js"></script>
<script type="text/javascript">
function fetch_progress(){
    $.get('progress.php',{ '<?php echo ini_get("session.upload_progress.name"); ?>' : 'test'}, function(data){
        var progress = parseInt(data);
        $('#progress .label').html(progress + '%');
        if(progress < 100){
            setTimeout('fetch_progress()', 100);    //当上传进度小于100%时,显示上传百分比
        }else{
            $('#progress .label').html('完成!'); //当上传进度等于100%时,显示上传完成
        }
    }, 'html');
}
 
$('#upload-form').submit(function(){
    $('#progress').show();
    setTimeout('fetch_progress()', 100);//每0.1秒执行一次fetch_progress(),查询文件上传进度
});
</script>

上面這段程式碼,就是透過JQ的ajax,每0.1秒回傳一次檔案上傳進度。並把進度百分比在div 標籤中顯示。

後台程式碼,需要分成兩個部分,upload.php處理上傳檔案。 progress.php 取得session中的上傳進度,並傳回進度百分比。

這裡檔案上傳就不再贅述,詳細步驟參考上文,upload.php:

<?php
if(is_uploaded_file($_FILES['file1']['tmp_name'])){                                            //判断是否是上传文件
    //unlink($_FILES['file1']['tmp_name']);    
    move_uploaded_file($_FILES['file1']['tmp_name'], "./{$_FILES['file1']['name']}");     //将缓存文件移动到指定位置
}
?>
主要关注progress.php:
<?php
/*
开启session。请注意在session_start()之前,请不要有想浏览器输出内容的动作,否则可能引起错误。
*/
 
session_start();
 
//ini_get()获取php.ini中环境变量的值
$i = ini_get('session.upload_progress.name');
 
//ajax中我们使用的是get方法,变量名称为ini文件中定义的前缀 拼接 传过来的参数
$key = ini_get("session.upload_progress.prefix") . $_GET[$i];    
//判断 SESSION 中是否有上传文件的信息
if (!empty($_SESSION[$key])) {                                        
    //已上传大小
    $current = $_SESSION[$key]["bytes_processed"];
    //文件总大小
    $total = $_SESSION[$key]["content_length"];
 
    //向 ajax 返回当前的上传进度百分比。
    echo $current < $total ? ceil($current / $total * 100) : 100;
}else{
    echo 100;                                                       
}
?>

到這裡,檔案進度的程式碼就已經完成了,配合前端,我們就可以做一個酷炫的檔案上傳功能啦!

學習心得:牢記檔案上傳的六個步驟

 


#下一節
<?php if ($_FILES['file']['error'] > 0) { switch ($_FILES['file']['error']) { //错误码不为0,即文件上传过程中出现了错误 case '1': echo '文件过大'; break; case '2': echo '文件超出指定大小'; break; case '3': echo '只有部分文件被上传'; break; case '4': echo '文件没有被上传'; break; case '6': echo '找不到指定文件夹'; break; case '7': echo '文件写入失败'; break; default: echo "上传出错<br/>"; } } else { $MAX_FILE_SIZE = 100000; if ($_FILES['file']['size'] > $MAX_FILE_SIZE) { exit("文件超出指定大小"); } $allowSuffix = array( 'jpg', 'gif', ); $myImg = explode('.', $_FILES['file']['name']); $myImgSuffix = array_pop($myImg); if (!in_array($myImgSuffix, $allowSuffix)) { exit("文件后缀名不符"); } $allowMime = array( "image/jpg", "image/jpeg", "image/pjpeg", "image/gif", ); if (!in_array($_FILES['file']['type'], $allowMime)) { exit('文件格式不正确,请检查'); } $path = "upload/images/"; $name = date('Y') . date('m') . date("d") . date('H') . date('i') . date('s') . rand(0, 9) . '.' . $myImgSuffix; if (is_uploaded_file($_FILEs['file']['tmp_name'])) { if (move_uploaded_file($_FILEs['file']['tmp_name'], $path . $name)) { echo "上传成功"; } else { echo '上传失败'; } } else { echo '不是上传文件'; } } ?>
章節課件