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

PHP AJAX 無刷新圖片上傳:從原生 XMLHttpRequest 到現代 Fetch API 實戰

     編輯:PHP 相關知識

無刷新上傳是提升用戶體驗的關鍵交互。早期依賴 iframe 或 FormData + XMLHttpRequest,現代瀏覽器原生支援 fetch + FormData,代碼更簡潔、錯誤處理更優雅。本文提供兩種實現方案,並涵蓋進度顯示、錯誤處理、安全驗證、多檔案上傳等生產級細節。

方案一:現代 Fetch API(推薦)

前端 HTML + JS

<!DOCTYPE html>
<html>
<head>
    
    AJAX 圖片上傳演示
    
</head>
<body>
    

圖片上傳演示

0%
</body> </html>

後端 PHP upload.php

<?php
header('Content-Type: application/json; charset=utf-8');

// 配置
$uploadDir = __DIR__ . '/uploads/';
$allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
$maxSize = 5 * 1024 * 1024; // 5MB
$maxFiles = 10;

if (!is_dir($uploadDir)) {
    mkdir($uploadDir, 0755, true);
}

$errors = [];
$uploaded = [];

foreach ($_FILES['images']['tmp_name'] as $index => $tmpName) {
    $error = $_FILES['images']['error'][$index];
    if ($error !== UPLOAD_ERR_OK) {
        $errors[] = "檔案 {$index}: 上傳錯誤代碼 {$error}";
        continue;
    }

    $name = $_FILES['images']['name'][$index];
    $size = $_FILES['images']['size'][$index];
    $mime = mime_content_type($tmpName); // 比 $_FILES['type'] 可靠

    // 伺服器端二次驗證
    if (!in_array($mime, $allowedMimes, true)) {
        $errors[] = "{$name}: 不支援的檔案類型 ({$mime})";
        continue;
    }
    if ($size > $maxSize) {
        $errors[] = "{$name}: 檔案過大 ({$size} bytes)";
        continue;
    }

    // 生成安全檔名:時間戳 + 隨機字串 + 原副檔名
    $ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
    $safeName = date('YmdHis') . '_' . bin2hex(random_bytes(8)) . ".{$ext}";
    $destPath = $uploadDir . $safeName;

    if (!move_uploaded_file($tmpName, $destPath)) {
        $errors[] = "{$name}: 移動檔案失敗";
        continue;
    }

    $uploaded[] = [
        'original_name' => $name,
        'saved_name'    => $safeName,
        'url'           => '/uploads/' . $safeName,
        'size'          => $size,
        'mime'          => $mime,
    ];
}

echo json_encode([
    'success' => empty($errors),
    'message' => empty($errors) ? '上傳成功' : '部分檔案上傳失敗',
    'data'    => $uploaded,
    'errors'  => $errors,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

方案二:原生 XMLHttpRequest(兼容舊瀏覽器)

核心邏輯與方案一的 uploadWithProgress 函數相同,僅差異在 FormData 建構與送出方式。現代專案建議直接使用方案一,僅在需支援 IE10 以下時考慮方案二。

生產級關鍵點總結

維度關鍵措施
安全驗證客戶端攔截 + 伺服器端 mime_content_type() 二次驗證 + 副檔名白名單 + 檔案大小限制
檔名安全絕不使用用戶原始檔名,採用 時間戳+隨機字串+副檔名 或 UUID
儲存隔離上傳目錄設定 php_flag engine off / Nginx location ~ \.php$ { deny all; } 禁止腳本執行
進度體驗XMLHttpRequest.upload.onprogress 實時更新進度條
錯誤處理網路錯誤、HTTP 非 2xx、業務失敗、格式錯誤分層處理,用戶可見錯誤訊息
多檔案multiple 屬性 + FormData.append('images[]', file) 陣列式上傳
大檔案分片上傳(WebUploader / Uppy / 自製 blob.slice + 合併 API)
CDN 直傳簽名直傳 OSS/S3,減輕應用伺服器頻寬壓力

常見坑位避雷

  • $_FILES['images']['type'] 來自瀏覽器,不可信,必須用 mime_content_type() 或 finfo_file() 驗證真實 MIME
  • move_uploaded_file() 目標目錄需 PHP 進程寫入權限,且不可在 Web 可執行目錄
  • 上傳目錄必須禁用 PHP 解析,防止上傳 .php.jpg 繞過檢查被執行
  • max_file_uploads (php.ini, 預設 20) 限制單次上傳檔案數
  • post_max_size 必須大於 upload_max_filesize

掌握細節,才能寫出既好用又安全的上傳功能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved