前言
這幾天在實作留言板,因此想了很多可以加上去的功能,目前這個留言板有註冊、登入、留言等的基本功能,但看著那空無一物的大頭照,就決定來做個讓使用者上傳大頭照的功能。
目標
不想使用原生的 input
按鈕,希望使用者不需要提交就能在圖片上傳之後自動改變大頭照,而在提交之後使用者也可以自由更改大頭照。
改變原生按鈕
會想這樣做是因為看到很多網站,都有很漂亮的按鈕或是樣式包含了上傳圖片的功能,這邊我直接前往這些網站去看他們的程式碼,來了解他們是怎麼做到的,而我發現他們是把上傳圖片的 input
給藏起來,並使用 label
標籤的 for
屬性來使用上傳圖片的功能。
<form method="POST" class="board__upload" id="img-load"
action="upload.php" enctype="multipart/form-data">
<input type="file" class="nodisplay" id="file" name="file" />
<label for="file" class="upload-body">照片</label>
</form>
這邊是用 nodisplay
這個 class 來隱藏 input
按鈕,這樣就可以自己做一個喜歡的樣式來取代它,不過這邊會有個小問題,那就是只有點擊一部分的地方才可以觸發事件,這邊我認為可能是有東西被擋住了按鈕,因此我加了z-index:1;
,就可以順利執行了。
上傳照片即跳轉
我們有一個可以上傳照片的按鈕後,接下來我們要讓表單在圖片上傳後自動跳轉至upload.php
,這樣我們就可以對圖片進行分析及處理
document.getElementById('file').onchange = function() {
document.getElementById('img-load').submit()
}
或是也可以在 input
內加入 onchange
屬性
<input type="file" id="file" class="nodisplay" name="file"
onchange="this.form.submit()" />
處理照片
這邊我們已經跳轉到了 upload.php
,首先我們要確認照片是否上傳成功,可以參考 PHP Manual 來對各項錯誤進行判斷。
接下來我們來判斷上傳的檔案的類型,我們不希望使用者上傳「非圖片類型」的資料,因此我們這樣處理
$error = $_FILES['file']['error'];
if ($error === 0) {
$tmpname = $_FILES['file']['tmp_name']; // 圖片暫存位置
$username = $_SESSION['username'];
$type = $_FILES['file']['type']; // 圖片類型
switch($type) {
case 'image/jpeg':
$safeType = true;
break;
case 'image/gif':
$safeType = true;
break;
case 'image/png':
$safeType = true;
break;
}
.
.
}
接下來我們使用 base64_encode
將圖片轉換成文字檔
if ($safeType) {
$file = fopen($tmpname, 'rb'); // 以二進位制開啟圖片
$fileContent = fread($file, filesize($tmpname)); // 讀取文件
fclose($file); // 關閉圖片
$fileContent = base64_encode($fileContent);// 將圖片編碼成 Base64 文字
.
.
}
上傳至資料庫
這邊因為我們要將 base64 編碼的文件傳至資料庫,因此我們需要 4 個數值 id
、type
、image
及 username
,另外 image
必須為 blob
類型,這邊我是使用 longblob
作為圖片存至資料庫的類型。
接下來我們做個判斷,判斷使用者是初次上傳照片還是要更換照片
$check = $conn->query("SELECT image FROM headshot WHERE username='$username'");
if ($check->num_rows === 1) {
$sql = sprintf(
"UPDATE headshot SET image='%s', type='%s' WHERE username='%s'",
$fileContent,
$type,
$username
);
} else {
$sql = sprintf(
"INSERT INTO headshot(image, username, type)
VALUES('%s', '%s', '%s')",
$fileContent,
$username,
$type
);
}
$result = $conn->query($sql);
header("Location: index.php");
這邊首先用 session 內的 username
判斷該使用者使否已上傳過大頭照,接著分別用 UPDATE 及 INSERT 來對資料庫進行操作,最後跳回首頁。
套用至討論區
現在狀況是討論區的各個使用者的訊息都已經顯示出來了,只差將照片依照不同使用者來套用至頭像,這邊我使用 nickname
去搜尋 username
(utils.php)
require_once('conn.php');
function getHeadShot($nickname) {
global $conn;
$result = '';
$getUsername = $conn->query("SELECT username FROM users WHERE nickname='$nickname'");
$username = $getUsername->fetch_assoc()['username'];
$getImageInfo = $conn->query("SELECT image FROM image WHERE username='$username'");
if ($getImageInfo->num_rows === 1) {
$img = $getImageInfo->fetch_assoc()['image'];
$type = $getImageInfo->fetch_assoc()['type'];
$result = '"data:' . $type . ';base64,' . $img;
}
return $result;
}
$result
是個字串,為 Data URI 的格式,Data URI 的格式是 data:[<mediatype>][;base64],<data>
。另外這邊考慮的點是如果使用者還未上傳照片,那就回傳空字串,接下我們就可以在「印出每個討論」的迴圈內把圖片一個一個套用至頭像
<?php
$result = $conn->query("SELECT * FROM comments ORDER BY id DESC");
while($row = $result->fetch_assoc()) {
$haveHeadShot = getHeadShot($row['nickname']);
if ($haveHeadShot) {
echo '<img class="comment__avatar" src=' . $haveHeadShot . '" />';
} else {
echo '<div class="comment__avatar"></div>';
}
}
?>
如此我們就可以依照 haveHeadShot
是否有值來判斷是否使用資料庫照片或是預設,並把照片套用至各個使用者。