[PHP]檔案上傳基礎實例練習

我有預感這篇會打很長...太多變數要說明了,而且這些還不是全部的內容
這邊先省略一些判斷,純粹的以最快的方式寫出一個上傳程式
而大多的上傳都是以此為藍圖而建構的



這篇一定要先下載檔案,再搭配文章做比較,比較好理解

首先為了讓文章可以好好呈現...我把文章裡出現的HTML標籤的都換成(、)
我試了各種方法包含code標籤都不能完整呈現
但是github上面的檔案是正確的,文章裡的程式碼就...看看說明就好
在WAMP下不需要自行設定資料夾權限,直接就可以使用上傳功能
接下來的內容請先去 github 頁面下載 file.html 和 file.php 兩隻程式
存在資料夾內,在資料夾內開一個新的資料夾叫做 upload 這個資料夾
完成之後你的上傳練習資料夾應該包含三個檔案

  1. file.html

  2. file.php

  3. upload (資料夾)


先說在檔案上傳的時候,前端的部分需要做的改變,也就是file.html和過去做的傳送資料最大的差別
只有兩個地方,便是form表單的設定,以及input type的設定
(form action="file.php" enctype="multipart/form-data" method="post") (input type="file" name="upfile")

可以看到form 增加了一個enctype屬性
並且賦予enctype="multipart/form-data內容,這是要上傳檔案的必備屬性

再來是input type屬性為file

用了這個屬性後,各家瀏覽器會用不同的方法顯示出讓使用者選擇檔案的介面
如此就把前端的頁面改成可以檔案上傳的屬性

再來是後端接收端,大部分的功能都寫在程式註解內了
把程式拆成幾個部分來看

一開始先看最外圍的if判斷式和內部變數設定
//判斷是否接收到上傳檔案,是則執行以下程式
if ($_FILES['upfile']['name']){
$path = "./upload/";//上傳的目標資料夾
$url = "http://127.0.0.1/note/upload_practice/upload/";
//上傳的目標絕對位置,記得結尾斜線!
$temp = $_FILES['upfile']['tmp_name'];//記憶體暫存

//$filename = $_FILES['upfile']['name'];
//使用使用這上傳的檔名直接儲存

$fileNameArray = explode('.',$_FILES['upfile']['name']);
//用explode方法把檔名藉由.拆成陣列,分割檔名和副檔名
$filename = $fileNameArray[0].date('Y-m-d').".".$fileNameArray[1];
//在原始檔名和副檔名中間插入日期格式,設定新的上傳檔名

if(){}........else{}.....;

//若是沒有選擇檔案就按下上傳,則印出以下內容
else {
echo "您沒有上傳檔案(a href=\"file.html\")回上一頁上傳(/a)";
}

最外層的if判斷是在判斷$_FILES,看起來和$_POST,$_GET有沒有很像呢?
沒錯,$_FILES就是在取得前端(input type="file")傳送來的資料
取得剛剛設定的name = "upfile"內的值,和使用者的檔名['name']
若是有選擇檔案,則為true,繼續執行
相反的如果沒上傳檔案,為空值NULL則執行最下面else的內容,將使用者引導回上一頁
也可以在if後面使用empty()方法來判斷是否為空值,但這邊我們不使用維持程式精簡

接著說明變數設定
$path = "./upload/";

這邊設定的是檔案上傳位置的變數,等一下會把檔案從暫存空間移動到這
$url = "http://127.0.0.1/note/upload_practice/upload/";

這是檔案儲存的絕對位置,之後要開啟檔案檢視就直接在此變數後面加上檔名就可以直接連結到檔案位置
$temp = $_FILES['upfile']['tmp_name'];

記憶體暫存位置,接收前端資料,並且賦予一個暫時的名稱tmp_name

再來說明的是檔案名稱,檔案名稱有兩種做法,直接使用原檔名上傳和管理者增加變數新增。
若使用者上傳的檔案,在上傳目錄上已經具有相同檔名的檔案
則新上傳的檔案會取代既有檔案,這時候一般會在檔名加入時間戳記來解決檔名重複問題

以下先示範不使用時間戳直接套用原始檔名的方法
$filename = $_FILES['upfile']['name'];

就這樣一行把檔名設定為從upfile接收到的$_FILES,並且保留原有name
若是要加上時間戳,會用到兩個常用的函數date()和explode()
$fileNameArray = explode('.',$_FILES['upfile']['name']);
$filename = $fileNameArray[0].date('Y-m-d').".".$fileNameArray[1];

首先用explode()把檔名藉由「.」拆成陣列,如此可以分割檔名和副檔名成陣列
例如:explode('.',自拍照.jpg)就會得到一個陣列 [0]=>自拍照,[1]=>jpg

接下來是在原始檔名和副檔名中間插入日期格式,設定新的上傳檔名
因為剛剛已經把原始檔名變成陣列,並且用串接date()的方式
把今天日期加上去,像這樣

陣列[0].date('Y-m-d').陣列[1];

就會得到新的檔名

自拍照2016-11-10.jpg;

要注意的是,連接副檔名的「.」也必須在設定檔名的時候串接起來
因為已經expode()成陣列,所以陣列中並不存在原本的「.」需要自行加入
兩種filename的設定方法擇一就可以了。

在這邊我們已經把各種變數都設定完成了,剩下最後一步就完成我們的檔案上傳功能了
也就是程式碼內部的那個迴圈
if (move_uploaded_file($temp,$path.$filename)){
//move_uploaded_file(暫存檔名,路徑,最終檔名)
echo "上傳成功看圖(a href=".$url.$filename.")看圖(/a)";
}else {
echo "上傳失敗(a href=\"file.html\")回上一頁上傳(/a)";
}

核心程式碼為
move_uploaded_file(暫存檔名,路徑,最終檔名)

這行是把剛剛的暫存檔移動到目的資料夾,並且使用剛剛設定的檔名 至於外層的if是用來確認檔案是否成功搬移
執行成功就到剛剛設定的儲存的絕對位置加上檔案名稱
就可以看到上傳的檔案

否則引導使用者回到上一頁去重新上傳,然後我們的上傳程式就這樣說明完啦!

關於上傳檔案也是很多相關的函數可以使用
像是防止使用者在檔名使用路徑的,或是判斷是不是有同名檔案存在的函數file_exists()等等
相信隨著做過的案子增加,不停地被需求甩在臉上就會慢慢進步
學習本來就是在解決問題中進步

留言