在用戶中心開發中,頭像上傳往往伴隨裁切需求:用戶上傳原圖後,需在前端選取合適區域、縮放比例,再生成最終頭像。cropper.js(或類似插件)基於 Canvas 實現了優秀的裁切交互體驗。本文聚焦於裁切完成後,如何將 Canvas 內容高效上傳至服務器的完整鏈路。
Canvas 提供 toDataURL() 方法,可將畫布內容導出為 data:image/png;base64,... 格式的 Data URL。前端通過 AJAX POST 發送該字符串,後端(以 PHP 為例)解碼 Base64 並寫入文件,即可實現「無文件上傳控件」的圖片上傳。
<script>
var canvas = document.getElementById("canvas_img");
var imgData = canvas.toDataURL("image/png"); // 可指定格式與質量
var xhr = new XMLHttpRequest();
xhr.open("POST", "save.php", true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log("上傳響應:", xhr.responseText);
}
};
// 發送 base64 字符串,建議 encodeURIComponent 防特殊字符
xhr.send("img=" + encodeURIComponent(imgData));
</script>
<?php
define('UPLOAD_DIR', __DIR__ . '/uploads/');
$base64 = $_POST['img'] ?? '';
if (!$base64) exit('無數據');
// 1. 去除 Data URL 頭部
$base64 = preg_replace('#^data:image/\w+;base64,#i', '', $base64);
// 2. 空格轉 +(URL 編碼中空格可能被編為 +)
$base64 = str_replace(' ', '+', $base64);
// 3. Base64 解碼
$binary = base64_decode($base64);
if ($binary === false) exit('Base64 解碼失敗');
// 4. 生成唯一文件名(日期目錄 + 隨機名)
$dateDir = date('Ymd');
$saveDir = UPLOAD_DIR . $dateDir;
if (!is_dir($saveDir)) mkdir($saveDir, 0755, true);
$fileName = $saveDir . '/' . bin2hex(random_bytes(8)) . '.png';
// 5. 寫入文件
$bytes = file_put_contents($fileName, $binary);
echo $bytes ? "成功保存至 $fileName ($bytes bytes)" : "寫入失敗";
?>
canvas.toBlob() 生成 Blob,配合 FormData 發送二進制流,避免 Base64 膨脹。canvas.toBlob(function(blob) {
var fd = new FormData();
fd.append('avatar', blob, 'avatar.png');
fetch('save.php', { method: 'POST', body: fd }).then(r => r.text()).then(console.log);
}, 'image/png', 0.9);
mime_content_type() 或 getimagesize() 確認為圖片。img.src = canvas.toDataURL() 即時展示裁切結果。XMLHttpRequest.upload.onprogress 或 fetch 配合 ReadableStream 實現。Canvas + Base64 + AJAX 是實現「裁切後直接上傳」的經典輕量方案,無需 <input type=file> 控件參與,流程順暢。生產環境建議升級為 Blob + FormData 以降低傳輸開銷,並務必做好後端驗證與存儲安全。掌握這套鏈路,即可輕松應對頭像裁切、簽名板保存、海報合成下載等豐富的前端圖像交互場景。