2015年3月23日

[教學] PHP & MySQL 學習筆記 第六堂(上傳檔案建置)

非常抱歉,關於部落格內 PHP 的部分目前已經停止維護,因本人已經很久沒有寫 PHP ,且文章中所使用的 PHP 版本偏舊,希望有心學習 PHP 的朋友們,可以參考 Codecademy 的課程,或近一步嘗試 Laravel 這個 PHP 框架(可透過 laracasts 學習),若有找不到錯的學習資源也歡迎在留言串分享,方便有需要的人能夠有更多學習的管道!
在很多地方我們都可以輕易的幫我們的檔案上傳到伺服器(雲端),在今天第六堂課,就要來學習如何建置一個簡單的上傳檔案系統。

先來看看最終成果
本次會學習到的函數共包含:
$_FILES['fileField']['type']:檔案類型
$_FILES['fileField']['size']:檔案大小(byte為單位)
$_FILES['fileField']['name']:檔案名稱
$_FILES['fileField']['tmp_name']:檔案暫存名稱
round(數值,位數):四捨五入
move_uploaded_file(檔案暫存名稱,搬移後的路徑與檔名):上傳檔案至伺服器
file_exists(路徑與檔名):判斷檔案是否存在
explode(字元,目標):將檔案名稱根據目標將以拆開
iconv(原來的編碼,轉換後的編碼,要轉換的字串):編碼轉譯(轉成中文編碼)

製作上傳視窗
首先打開Dream Weaver,建立兩個分名為p6-1.php和p6-2.php的檔案。

一樣先從「設計」的地方來編輯一個檔案上傳的介面,利用最左邊的表單,還有右邊兩個「檔案欄位」及「按鈕」

就可以建立好一個上傳的頁面

接著利用下方的小箱子,把這個表單送出的時候,可以傳到p6-2.php

這裡點一下「瀏覽」這個按鈕時,我們會看到這個按鈕的名稱(fileField),這是我們之後要在p6-2.php當中,讀取的檔案名稱。

上傳檔案語法撰寫
再來就要跳到p6-2.php來撰寫語法了,上傳的指令共會用到這些語法:

$_FILES['fileField']['type']:檔案類型
$_FILES['fileField']['size']:檔案大小(byte為單位)
$_FILES['fileField']['name']:檔案名稱
$_FILES['fileField']['tmp_name']:檔案暫存名稱

我們在網頁的最上方,輸入這些語法,並且建立成變數
<pre class="codeblock prettyprint">
<?php
$type=$_FILES['fileField']['type'];
$size=$_FILES['fileField']['size'];
$name=$_FILES['fileField']['name'];
$tmp_name=$_FILES['fileField']['tmp_name'];
?>


接著,我們在網頁內文(body)的地方,去呼叫這些變數

如果順利的話,你應該會看到上傳檔案的結果畫面

第一個是上傳pdf檔(這時候並沒有真正上傳,只是暫存到temp裡面)

第二個是上傳excel 2003的檔案(.xls)
撰寫語法判斷檔案類型

接下來,我們要寫一段語法,這段語法會幫我們判斷檔案的類型,如果符合檔案類型,我們才讓它上傳上來,不符合的,就不能上傳。

這裡我們要先把允許的檔案上傳一次看看,我們才會知道他的檔案類型名稱,像是透透剛剛的上傳,我們才知道pdf的檔案名稱為"application/pdf";excel2003的檔案名稱為"application/vnd.ms-excel"。

這段語法的意思是,如果(if)檔案類型($type)為pdf檔(application/pdf)或excel檔(application/vnd.ms-excel)時則顯示上傳成功,否則(else)則顯示上傳失敗。
或的語法是「||」,和的語法是「&&」。
if的函式是若符合()內的條件,則執行{}內的功能。


這時候你可以試試看,當我上傳的是excel檔時,會顯示成功

當我上傳的是jpg檔時,則會顯示失敗
限制檔案上傳容量

這時候,我希望限制檔案上傳容量的上限,免得有網友亂傳抄大的檔案給我,讓我的伺服器癱瘓,寫法如下:

檔案大小的變數我們把它訂做size,而且是用byte計算,10240000byte=1MB,這裡我希望限制檔案上傳的容量限制在10MB(10240000byte)以下。

寫法是在if中間再寫一個if,也就是「若我檔案符合規定」再檢驗「若我的檔案小於10MB」。

找一個容量大一點的試試看,因為這個檔案容量超過10MB,所以上傳失敗!
如果你覺得看byte實在是太不習慣了,你也可以利用php裡面的一些數學運算來處理,這裡我建立一個新的變項名稱,叫做sizemb,是將原本的size除以1024000,也就是變成mb的單位,同時為了避免小數點太多的關係,我用了一個round(數值,位數)函數,請它幫我將裡面的數值四捨五入到小數點第二位。最後記得修改echo的地方,改成新的echo變項,sizemb。

這時候,你就會看到熟悉的mb這個單位啦!
上傳檔案

要將檔案從temp真正搬上我們的伺服器時,我們要使用到這個函式:
move_uploaded_file(檔案暫存名稱,搬移後的路徑與檔名)

首先,我們要在php的資料夾(或者我們的伺服器)中,建立一個資料夾,這個資料夾是給上傳的檔案儲存用的。

然後,我們要加入剛剛那段語法,括弧前面放的是暫存檔案名稱,後面放的是儲存的路徑和檔名

成功的話,現在到你剛剛新建的資料夾裡,就可以看到上傳檔案了!



判斷是否有相同檔名的名稱

為了避免相同檔名的名稱會覆蓋掉原本的檔案,我們必須要想一些辦法,一種方法是當檔名相同時,我們就不讓網友上傳;另一種則是當檔名相同時,我們幫它改成其他的檔名。

判斷檔案是否存在時,我們會用到這個函式file_exists(路徑與檔名)

第一種作法相當簡單,一樣透過寫假設語句,當檔名重複時,顯示檔名重複且不上傳,否則,顯示上傳成功且上傳。

如果圖樣的檔名已經上傳的過化,則會出現提示訊息

第二種方法比較複雜一點,網友不必自己去修改檔名,對於使用者的經驗感受上會比較好,這種作法,我們會用到另一個函式explode(字元,目標),這是用再分割檔案名稱的,我們接下來看下去。

首先我們要把檔案名稱分隔,並把它取名為變項file,這裡是把變項name根據「.」加以分割,因為我要分成主檔名和副檔名,分割後的檔案是一個矩陣,讀取主檔名時用file[0],讀取副檔名時用file[1]。

我們可以利用echo測試看看

echo後,會看到根據「.」把name分成了前後兩部分
 這時候,我們可以根據年月日時分秒(ymdhis)來修改檔案名稱,這樣應該不會重複了吧!

上傳後,如果檔名重複,就會自動修改檔名,在原檔名後面加上年月日十分秒

如果你還是不放心,可能有網友會同時間上傳,那麼你可以在後面再加一個隨機數字,我這裡是取0~10。

檔名最後就會多一個隨機數字。
 最後加上move_uploaded_file這個函式,讓它是真正上傳到伺服器,名稱的地方要注意,記得要在新名稱(new_name)後面補回原本的「.」和副檔名(file[1])。

上傳看看
 沒問題的話,上傳資料夾中就會自動有一個新檔名的名稱了

寫到這裡其實就已經大功告成了
完整的程式碼如下:
<?php
$type=$_FILES['fileField']['type'];
$size=$_FILES['fileField']['size'];
$name=$_FILES['fileField']['name'];
$tmp_name=$_FILES['fileField']['tmp_name'];
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上傳結果</title>
</head>

<body>
<?php
$sizemb=round($size/1024000,2);
echo "檔案類型:".$type."</br>";
echo "檔案大小:".$sizemb."MB</br>";
echo "檔案名稱:".$name."</br>";
echo "暫存名稱:".$tmp_name."</br>";
if($type=="application/pdf" || $type=="application/vnd.ms-excel"){
 if($sizemb < 3){
  if(file_exists("uploadfiles/".$name)){
   $file=explode(".",$name);
   //echo $file[0];/*主檔名*/
   //echo $file[1];/*副檔名*/
   $new_name=$file[0]."-".date(ymdhis)."-".rand(0,10);
   echo "</br>已修改為新檔名:".$new_name."後上傳成功";
   move_uploaded_file($tmp_name,"uploadfiles/".$new_name.".".$file[1]);
  }else{
   move_uploaded_file($tmp_name,"uploadfiles/".$name);
   echo "上傳成功";
  }
 }else{
  echo "檔案太大,上傳失敗";
 }
}else{
 echo "檔案格式錯誤,上傳失敗";
}
?>
</body>
</html>




上傳檔案後自動修改檔名
在上面的例子中,電腦會根據檔名是否上傳過來修改檔案名稱,但現在更常用的方法,其實是不管什麼檔案名稱,我都直接幫你修改成,這樣就不用判斷這個檔案是不是有重複過了。

來試試看吧,我們再開一個新的檔案是p6-3.php,然後把p6-2的程式碼複製貼上。

只要把if file_exists這段拿掉,else也拿掉後,就可以了

像是這樣

程式碼如下

<body>
<?php
$sizemb=round($size/1024000,2);
echo "檔案類型:".$type."</br>";
echo "檔案大小:".$sizemb."MB</br>";
echo "檔案名稱:".$name."</br>";
echo "暫存名稱:".$tmp_name."</br>";
if($type=="application/pdf" || $type=="application/vnd.ms-excel"){
 if($sizemb < 3){
  $file=explode(".",$name);
  $new_name=$file[0]."-".date(ymdhis)."-".rand(0,10);
  echo "</br>已修改為新檔名:".$new_name."後上傳成功";
  move_uploaded_file($tmp_name,"uploadfiles/".$new_name.".".$file[1]);
  echo "上傳成功";
 }else{
  echo "檔案太大,上傳失敗";
 }
}else{
 echo "檔案格式錯誤,上傳失敗";
}
?>
</body>


中文檔案上傳
然而,剛剛的語法我們會碰到一個問題,就是當我上傳中文檔名的文件時會出現問題,像是這樣:
雖然它顯示上傳成功,但實際上是沒有上傳的。

所以我們會需要使用iconv(原來的編碼,轉換後的編碼,要轉換的字串)這個函式來轉碼。

我們可以這樣想像,網頁讀取中文字時需要用UTF-8來讀取;但是電腦讀取中文字時需要用BIG-5來讀取。

所以當網頁用UTF-8上傳中文檔名時,電腦會讀不出來,因此,我們要把網頁上的UTF-8轉成BIG-5讓電腦來判讀。

在網頁最上面的地方,我們把原來的name轉碼從UTF-8轉成BIG-5的格式,讓電腦可以讀取。
同時在新增一個變數,是讓網頁上echo出檔案名稱用的,取名叫做nameEcho,這是給網頁讀取的。

這時候,上傳的檔案名稱我是呼叫nameEcho,這是UTF-8的格式所以可以呈現出來,但是下面「已修改為...」的地方,那裡是我轉成BIG-5後的中文,網頁會讀不出來。

為了解決這樣的問題,我要再把我的程式碼修改一下,在echo之前,把BIG-5轉回UTF-8,讓網頁可以讀取。

沒有問題的話,就可以正確顯示中文,而且也可以正確上傳中文檔名了。
完整的程式碼如下:
<?php
$type=$_FILES['fileField']['type'];
$size=$_FILES['fileField']['size'];
$name=iconv("UTF-8","BIG-5",$_FILES['fileField']['name']);
$nameEcho=$_FILES['fileField']['name'];
$tmp_name=$_FILES['fileField']['tmp_name'];
?>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="https://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>上傳結果</title>
</head>

<body>
<?php
$sizemb=round($size/1024000,2);
echo "檔案類型:".$type."</br>";
echo "檔案大小:".$sizemb."MB</br>";
echo "檔案名稱:".$nameEcho."</br>";
echo "暫存名稱:".$tmp_name."</br>";
if($type=="application/pdf" || $type=="application/vnd.ms-excel"){
 if($sizemb < 3){
  $file=explode(".",$name);
  $new_name=$file[0]."-".date(ymdhis)."-".rand(0,10);
  $chi_name=iconv("BIG-5","UTF-8",$new_name);
  echo "</br>已修改為新檔名:".$chi_name."後上傳成功";
  move_uploaded_file($tmp_name,"uploadfiles/".$new_name.".".$file[1]);
  echo "上傳成功";
 }else{
  echo "檔案太大,上傳失敗";
 }
}else{
 echo "檔案格式錯誤,上傳失敗";
}
?>
</body>
</html>

我們可以來看看成果


以上內容均為本人在馬老師雲端研究室學習所整理之筆記

2 則留言:

  1. 您好
    小弟參考您的方法來建立一個上傳的網頁,
    僅針對PDF做限制,
    但在上傳過程中,有些PDF會顯示格式錯誤,
    有些就顯示正常上傳,
    想請問這個該如何解決呢?
    不好意思,麻煩您了,謝謝

    回覆刪除
  2. 您好~~
    請問要讀取uploadfiles/裡面所有的圖秀出,該怎麼寫語法?
    我用 mysqli_fetch_assoc 無法讀出資料...
    想請問這個該如何解決呢?
    不好意思,麻煩您了,謝謝

    回覆刪除