2019/11/16 (更新內容)
2024/11/20 (更新內容)
2024/11/27 (更新內容)
php.ini (先確認一下是否將file_uploads設為On),也注意一下可上傳檔案的大小限制。
;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;
; Whether to allow HTTP file uploads.
; http://php.net/file-uploads
file_uploads = On
; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
; http://php.net/upload-tmp-dir
; upload_tmp_dir =
; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 2M
; Maximum number of files that can be uploaded via a single request
max_file_uploads = 20
首先,先新增一個form,因為要上傳檔案,enctype要設定為multipart/form-data,另外,利用file元件讓使用者選擇檔案。
fileupload.php
<!DOCTYPE html>
<html>
<body>
<form action="fileupload.php" method="post" enctype="multipart/form-data">
選擇圖片:
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="上傳圖片" name="submit">
</form>
</body>
</html>
php會把上傳的檔案及相關資料放到$_FILES
fileupload.php:
<?php
var_dump($_FILES);
?>
<!DOCTYPE html>
<html>
<body>
<form action="fileupload.php" method="post" enctype="multipart/form-data">
選擇圖片:
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="上傳圖片" name="submit">
</form>
</body>
</html>
我們會看到類似這樣的內容:
array(1) { ["fileToUpload"]=> array(6) { ["name"]=> string(11) "im_logo.png" ["full_path"]=> string(11) "im_logo.png" ["type"]=> string(9) "image/png" ["tmp_name"]=> string(24) "C:\xampp\tmp\php3103.tmp" ["error"]=> int(0) ["size"]=> int(2159) } }
基本上,$_FILES裡包含一個陣列,陣列的名稱是我們給file元件的名稱(fileToUpload),陣列內容包括檔案的名稱、資料型態、暫存檔的名稱、錯誤代碼、檔案大小。
基本上檔案已經上傳成功了,只是,目前是暫存在暫存目錄中,所以我們要把檔案移到apache伺服器下的目錄,PHP提供了一個指令move_uploaded_file來進行這個動作 (詳參: move_uploaded_file() Function)。
fileupload_process.php的內容改成:
<?php
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $_FILES["fileToUpload"]["name"]);
header('location:fileupload.php');
?>
基本上就可以從檔案管理員裡看到檔案了。
但是,最好是將圖片集中在一個檔案夾裡。先產生資料夾,程式碼要改成:
$target_dir = "uploads/";
$filename = $target_dir.basename($_FILES["fileToUpload"]["name"]);
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $filename);
接下來,我們來修改一下fileupload.php
<?php
$target_dir = "uploads/";
$filename = $target_dir.basename($_FILES["fileToUpload"]["name"]);
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $filename);
?>
<!DOCTYPE html>
<html>
<body>
<form action="fileupload.php" method="post" enctype="multipart/form-data">
選擇圖片:
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="上傳圖片" name="submit">
</form>
<img src="<?=$filename?>" alt="<?=$filename?>" width="30%">
</body>
</html>
看到圖片了!
一般而言,都會在php.ini中設定了檔案大小的上限,所以,當我們傳了一個超過2MB的檔案,會看不到內容。
怎麼知道有錯誤發生呢? 可以檢查$_FILES["fileToUpload"]["error"],當內容是0的時候,就是沒有錯誤,當內容是1的時候,就是檔案超過2MB (詳參: Error Messages Explained & POST method uploads)。
fileupload.php
<?php
$target_dir = "uploads/";
$filename = $target_dir.basename($_FILES["fileToUpload"]["name"]);
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $filename);
$error = $_FILES["fileToUpload"]["error"];
?>
<!DOCTYPE html>
<html>
<body>
<form action="fileupload.php" method="post" enctype="multipart/form-data">
選擇圖片:
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="上傳圖片" name="submit">
</form>
<img src="<?=$filename?>" alt="<?=$filename?>" width="30%">
<?= $error ?>
</body>
</html>
錯誤訊息是個數字,修改fileupload.php,處理錯誤訊息。
<?php
$target_dir = "uploads/";
$filename = $target_dir.basename($_FILES["fileToUpload"]["name"]);
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $filename);
$error = $_FILES["fileToUpload"]["error"];
$phpFileUploadErrors = [
0 => '上傳成功',
1 => '檔案大小超過伺服器設定(2MB)!',
2 => '檔案大小超過瀏覽器設定!',
3 => '上傳檔案不完整',
4 => '未上傳檔案',
6 => '暫存資料夾不存在',
7 => '無法寫入檔案',
8 => 'PHP擴充導致檔案無法上傳',
];
$msg = $phpFileUploadErrors[$error]??"";
?>
<!DOCTYPE html>
<html>
<body>
<form action="fileupload.php" method="post" enctype="multipart/form-data">
選擇圖片:
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="上傳圖片" name="submit">
</form>
<img src="<?=$filename?>" alt="<?=$filename?>" width="30%">
<?= $msg ?>
</body>
</html>
避免第一次打開網頁會有錯誤訊息,程式碼改成:
fileupload.php
<?php
$target_dir = "uploads/";
$filename = "";
$msg="";
if ($_FILES){
// var_dump($_FILES);
$filename = $target_dir.basename($_FILES["fileToUpload"]["name"]);
// echo $filename;
move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $filename);
$error = $_FILES["fileToUpload"]["error"];
$phpFileUploadErrors = [
0 => '上傳成功',
1 => '檔案大小超過伺服器設定(2MB)!',
2 => '檔案大小超過瀏覽器設定!',
3 => '上傳檔案不完整',
4 => '未上傳檔案',
6 => '暫存資料夾不存在',
7 => '無法寫入檔案',
8 => 'PHP擴充導致檔案無法上傳',
];
$msg = $phpFileUploadErrors[$error]??"";
}
?>
<!DOCTYPE html>
<html>
<body>
<form action="fileupload.php" method="post" enctype="multipart/form-data">
選擇圖片:
<input type="file" name="fileToUpload" id="fileToUpload"><br>
<input type="submit" value="上傳圖片" name="submit">
</form>
<img src="<?=$filename?>" alt="<?=$filename?>" width="30%">
<?= $msg ?>
</body>
</html>
提供file upload之後,會引發不少資安漏洞:
避免被注入php程式碼並執行指令:
disable_functions = exec, system, eval
避免資安問題,可以做一些基本的檢查:
// 上傳圖片
<?php
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
// 檔案上傳
$target_dir = "uploads/";
//
$target_file = $target_dir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file, PATHINFO_EXTENSION));
// 檢查是否為圖片檔案
$check = getimagesize($_FILES["fileToUpload"]["tmp_name"]);
if ($check !== false) {
echo "檔案是圖片 - " . $check["mime"] . "。";
$uploadOk = 1;
} else {
echo "檔案不是圖片。";
$uploadOk = 0;
}
// 檢查檔案是否已存在
if (file_exists($target_file)) {
echo "抱歉,檔案已存在。";
$uploadOk = 0;
}
// 檢查檔案大小
if ($_FILES["fileToUpload"]["size"] > 500000) {
echo "抱歉,您的檔案過大。";
$uploadOk = 0;
}
// 允許的檔案格式
if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg" && $imageFileType != "gif") {
echo "抱歉,只允許 JPG, JPEG, PNG & GIF 檔案。";
$uploadOk = 0;
}
// 檢查 $uploadOk 是否為 0
if ($uploadOk == 0) {
echo "抱歉,您的檔案無法上傳。";
} else {
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)) {
echo "檔案 " . htmlspecialchars(basename($_FILES["fileToUpload"]["name"])) . " 已成功上傳。";
} else {
echo "抱歉,上傳檔案時發生錯誤。";
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>上傳圖片</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
選擇要上傳的圖片:
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" value="上傳圖片" name="submit">
</form>
</body>
</html>
如果每個產品都有對應的一張圖片,可以將圖片檔名存為產品編號,這樣就不用另外新增欄位。但是,如果使用者上傳時會有不同的格式,那就要儲存檔案名稱。
如果每個產品會有多張對應圖片,那又要如何處理?
要如何在瀏覽產品時,看到產品對應圖片?
檢查檔案類型很重要,我們不希望收到一個無法顯示的圖檔,甚至是個會把檔案都刪掉的執行檔(被植入木馬)
檢查檔案是否為圖檔
網頁上範例很多,但是因為email的服務為了控制垃圾信件越管越嚴,所以,多數範例的說明都已過時,所以,多數人以為傳送Email相當困難,其實,只要找到對的範例,跟著做就行了。
調低安全性的作法已經被gmail停用了,接下來應用程式密碼即將被停用
Developer’s Tutorial on Sending Emails using PHPMailer and Gmail SMTP
要先設定Gmail
透過App Password,較簡單,但是未來會被停用
OAuth2,較安全
下載PHPMailer
利用composer下載,未來可更新,較安全
請更新PHPMailer到最新版,以避免安全漏洞
直接下載檔案
寫程式
設定連線
設定送信者
設定收信者
設定信件內容
送出
以App Password送出信件:
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once 'vendor/autoload.php';
$mail = new PHPMailer(true);
//Configure an SMTP
$mail->isSMTP();
$mail->Host = ‘smtp.gmail.com’;
$mail->SMTPAuth = true;
$mail->Username = ‘sender@gmail.com’;
$mail->Password = ‘App Password’;
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
$mail->Post = 587;
// Sender information
$mail->setFrom('FROM_EMAIL_ADDRESS', 'FROM_NAME');
// Multiple recipient email addresses and names
// Primary recipients
$mail->addAddress('RECIPIENT_EMAIL_ADDRESS_1', 'RECIPIENT_NAME_1');
$mail->addAddress('RECIPIENT_EMAIL_ADDRESS_2', 'RECIPIENT_NAME_2');
// Adding CC recipients
$mail->addCC('CC_EMAIL_ADDRESS_1', 'CC_NAME_1');
$mail->addCC('CC_EMAIL_ADDRESS_2', 'CC_NAME_2');
// Adding BCC recipients
$mail->addBCC('BCC_EMAIL_ADDRESS_1', 'BCC_NAME_1');
$mail->addBCC('BCC_EMAIL_ADDRESS_2', 'BCC_NAME_2');
$mail->isHTML(false);
$mail->Subject = 'PHPMailer SMTP test';
$mail->Body = "PHPMailer the awesome Package\nPHPMailer is working fine for sending mail\nThis is a tutorial to guide you on PHPMailer integration";
// Attempt to send the email
if (!$mail->send()) {
echo 'Email not sent. An error was encountered: ' . $mail->ErrorInfo;
} else {
echo 'Message has been sent.';
}
$mail->smtpClose();