首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

JavaScript + PHP 非同步圖片上傳 (XMLHttpRequest / Fetch)

     編輯:PHP 相關知識

後端 PHP (upload.php)

<?php
header('Content-Type: application/json');

if (!isset($_FILES['myfile'])) {
    echo json_encode(['code'=>1, 'msg'=>'No file']);
    exit;
}

$file = $_FILES['myfile'];
$allowTypes = ['image/jpeg','image/png','image/gif','image/webp'];
$maxSize = 5 * 1024 * 1024; // 5MB

if (!in_array($file['type'], $allowTypes)) {
    echo json_encode(['code'=>2, 'msg'=>'Type not allowed']);
    exit;
}
if ($file['size'] > $maxSize) {
    echo json_encode(['code'=>3, 'msg'=>'File too large']);
    exit;
}
if ($file['error'] !== UPLOAD_ERR_OK) {
    echo json_encode(['code'=>4, 'msg'=>'Upload error: '.$file['error']]);
    exit;
}

// 目錄:uploads/2024/01/15/
$dateDir = date('Y/m/d');
$uploadDir = __DIR__ . "/uploads/$dateDir";
if (!is_dir($uploadDir)) mkdir($uploadDir, 0755, true);

// 重新命名避免衝突
$ext = pathinfo($file['name'], PATHINFO_EXTENSION);
$newName = bin2hex(random_bytes(8)) . '.' . $ext;
$dest = "$uploadDir/$newName";

if (move_uploaded_file($file['tmp_name'], $dest)) {
    // 回傳相對路徑供前端顯示
    $url = "/uploads/$dateDir/$newName";
    echo json_encode(['code'=>0, 'url'=>$url, 'name'=>$file['name']]);
} else {
    echo json_encode(['code'=>5, 'msg'=>'Move failed']);
}
?>

前端:原生 Fetch API (現代瀏覽器)

<input type="file" id="fileInput" accept="image/*" />
<button id="uploadBtn">上傳</button>
<div id="preview"></div>
<div id="progress"></div>

<script>
document.getElementById('uploadBtn').addEventListener('click', async () => {
    const file = document.getElementById('fileInput').files[0];
    if (!file) return alert('請選擇檔案');

    const formData = new FormData();
    formData.append('myfile', file);

    const progressEl = document.getElementById('progress');
    progressEl.textContent = '上傳中...';

    try {
        const res = await fetch('upload.php', {
            method: 'POST',
            body: formData
            // 不需手動設定 Content-Type,瀏覽器會自動加上 boundary
        });
        const data = await res.json();
        
        if (data.code === 0) {
            progressEl.textContent = '成功!';
            document.getElementById('preview').innerHTML = 
                `${data.name}`;
        } else {
            progressEl.textContent = '失敗: ' + data.msg;
        }
    } catch (e) {
        progressEl.textContent = '網路錯誤: ' + e.message;
    }
});
</script>

前端:XMLHttpRequest (支援 IE10+、進度條)

function uploadWithXHR(file, onProgress) {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', 'upload.php');
        
        xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
                onProgress(Math.round(e.loaded / e.total * 100));
            }
        };
        
        xhr.onload = () => {
            if (xhr.status === 200) resolve(JSON.parse(xhr.responseText));
            else reject(new Error('HTTP ' + xhr.status));
        };
        xhr.onerror = () => reject(new Error('Network error'));
        
        const fd = new FormData();
        fd.append('myfile', file);
        xhr.send(fd);
    });
}

// 使用
uploadWithXHR(file, pct => progressEl.textContent = pct + '%')
    .then(data => { /* ... */ })
    .catch(err => { /* ... */ });

多檔案上傳

<input type="file" id="files" multiple accept="image/*" />

const files = [...document.getElementById('files').files];
for (const file of files) {
    // 依序或平行上傳
    await uploadWithXHR(file, pct => console.log(file.name, pct + '%'));
}

安全性檢查清單

  • ✅ 檔案型態雙重驗證 (MIME + getimagesize()/finfo)
  • ✅ 檔案大小限制 (PHP + 前端雙重)
  • ✅ 重新命名 (隨機雜湊 + 保留副檔名)
  • ✅ 儲存目錄不可執行 PHP (Nginx: location ~ \.php$ { deny all; })
  • ✅ HTTPS 傳輸
  • ✅ CSRF Token 驗證 (AJAX 請求攜帶 Header)
  • ✅ 登入狀態驗證 (Session/JWT)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved