無需 jQuery、插件,純原生 JavaScript 配合 PHP 即可實現現代異步圖片上傳(支持進度條、拖拽、多文件擴展)。本文提供精簡完整的前後端代碼。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HTML5 Ajax 圖片上傳</title>
<style>
.txt { height:28px; border:1px solid #cdcdcd; width:670px; }
.mybtn { background:#fff; border:1px solid #cdcdcd; height:30px; width:70px; cursor:pointer; }
.file { position:absolute; top:0; right:80px; height:24px; opacity:0; width:260px; cursor:pointer; }
</style>
</head>
<body>
<label>圖片:</label>
<input type="text" id="textfield" class="txt" readonly />
<input type="file" id="file" class="file" onchange="document.getElementById('textfield').value=this.value" />
<button class="mybtn" onclick="UpladFile()">上傳</button>
<div id="progress" style="margin-top:10px; display:none;">
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
<span id="progressText">0%</span>
</div>
<script>
var xhr;
function createXMLHttpRequest() {
if (window.ActiveXObject) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
}
function UpladFile() {
var fileObj = document.getElementById("file").files[0];
if (!fileObj) { alert("請選擇文件"); return; }
var form = new FormData();
form.append("myfile", fileObj);
createXMLHttpRequest();
xhr.upload.onprogress = function(e) {
if (e.lengthComputable) {
var percent = Math.round((e.loaded / e.total) * 100);
document.getElementById("progressBar").value = percent;
document.getElementById("progressText").innerText = percent + "%";
}
};
xhr.onreadystatechange = handleStateChange;
xhr.open("POST", "upload.php", true);
document.getElementById("progress").style.display = "block";
xhr.send(form);
}
function handleStateChange() {
if (xhr.readyState == 4) {
document.getElementById("progress").style.display = "none";
if (xhr.status == 200 || xhr.status == 0) {
var result = xhr.responseText;
try {
var json = JSON.parse(result);
if (json.file) {
alert('上傳成功!圖片鏈接:\n' + json.file);
} else {
alert('上傳失敗:' + (json.error || '未知錯誤'));
}
} catch (e) {
alert('響應解析失敗:' + result);
}
} else {
alert('請求失敗:HTTP ' + xhr.status);
}
}
}
</script>
</body>
</html>
<?php
if (isset($_FILES["myfile"])) {
$ret = ['error' => '', 'file' => ''];
$uploadDir = 'images' . DIRECTORY_SEPARATOR . date("Ymd") . DIRECTORY_SEPARATOR;
$dir = __DIR__ . DIRECTORY_SEPARATOR . $uploadDir;
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
chmod($dir, 0777);
}
// 單文件處理
$ext = strtolower(pathinfo($_FILES["myfile"]["name"], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (!in_array($ext, $allowed)) {
$ret['error'] = '不支持的文件類型';
} elseif ($_FILES["myfile"]["error"] !== UPLOAD_ERR_OK) {
$ret['error'] = '上傳錯誤代碼:' . $_FILES["myfile"]["error"];
} else {
$fileName = time() . '_' . uniqid() . '.' . $ext;
$dest = $dir . $fileName;
if (move_uploaded_file($_FILES["myfile"]["tmp_name"], $dest)) {
$ret['file'] = $uploadDir . $fileName; // 返回相對路徑
} else {
$ret['error'] = '文件移動失敗,請檢查權限';
}
}
header('Content-Type: application/json; charset=utf-8');
echo json_encode($ret);
exit;
}
?>
| 技術 | 說明 |
|---|---|
FormData | 構建 multipart/form-data 請求體,自動設置 Content-Type: multipart/form-data; boundary=... |
xhr.upload.onprogress | 監聽上傳進度事件,實現進度條(需服務端不緩存請求體,Nginx 需 proxy_request_buffering off;) |
move_uploaded_file | 安全移動臨時文件,防止臨時文件被惡意訪問 |
uniqid() + time() | 生成唯一文件名,避免衝突 |
| 日期目錄 | images/20240115/ 避免單目錄文件過多影響性能 |
files 迴圈 form.append("myfile[]", file),後端 $_FILES['myfile']['name'] 為數組。drop 事件,e.dataTransfer.files 獲取文件列表。URL.createObjectURL(file) 生成臨時 URL 顯示縮略圖。slice() 分片上傳,斷點續傳需後端支持合併。finfo_file)、圖片重繪 (imagecreatefromstring)、病毒掃描集成。FormData、XMLHttpRequest Level 2。FormData Polyfill 或回退至 iframe 方案。原生 JS 實現異步上傳,零依賴、體積極小、完全可控,是輕量級項目的最佳選擇。