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

PHP 無刷新文件上傳兩大方案實戰:iframe 傳統法與 AJAX 現代法完整對比

     編輯:PHP 相關知識

前言

「無刷新上傳」本質是不導航當前頁面完成文件提交並獲取響應。歷史上主流有兩種實現:iframe 表單提交(兼容性極佳,IE6+)與 AJAX (XMLHttpRequest/Fetch) + FormData(現代標準,支持進度條)。本文提供兩套完整可運行代碼,並分析優缺點與適用場景。

方案一:iframe 隱形提交(傳統通用)

核心原理

  • 頁面放一個 <iframe name="uploader" style="display:none">。
  • <form target="uploader"> 指向 iframe,提交後頁面不跳轉。
  • 服務端響應一段 <script>parent.callback(data)</script>,調用父頁面回調。

前端頁面 (index.html)

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>iframe 上傳演示</title></head>
<body>
<h3>iframe 無刷新上傳</h3>
<iframe name="uploader" style="display:none;"></iframe>

<form action="upload_iframe.php" method="post" enctype="multipart/form-data" target="uploader">
    <input type="file" name="upfile" required>
    <button type="submit">上傳</button>
</form>

<div id="msg" style="margin-top:10px;color:green"></div>

<script>
function uploadCallback(res) {
    document.getElementById('msg').textContent = res.success ? '上傳成功:' + res.url : '失敗:' + res.error;
}
</script>
</body></html>

後端處理 (upload_iframe.php)

<?php
header('Content-Type: text/html; charset=utf-8');
$targetDir = __DIR__ . '/uploads/';
if (!is_dir($targetDir)) mkdir($targetDir, 0755, true);

$resp = ['success' => false];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upfile'])) {
    $file = $_FILES['upfile'];
    if ($file['error'] === UPLOAD_ERR_OK) {
        $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
        $newName = uniqid('img_') . '.' . $ext;
        $dest = $targetDir . $newName;
        if (move_uploaded_file($file['tmp_name'], $dest)) {
            $resp = ['success' => true, 'url' => '/uploads/' . $newName];
        } else {
            $resp['error'] = '移動文件失敗';
        }
    } else {
        $resp['error'] = '上傳錯誤碼:' . $file['error'];
    }
}
?>
<script>
window.parent.uploadCallback();
</script>

優缺點總結

優點缺點
兼容所有瀏覽器(含 IE6)無法獲取上傳進度
代碼極簡,無需 JS 庫需服務端輸出 JS 腳本,耦合度高
天然支持跨域(同域策略下)錯誤處理困難,難以區分網絡錯誤與業務錯誤
可在舊項目零成本接入響應需嵌入 <script>,易發生 XSS 若未轉義

方案二:AJAX + FormData(現代標準,推薦)

核心原理

  • FormData 自動構造 multipart/form-data 體。
  • XMLHttpRequest.upload.onprogress 或 fetch 配合 ReadableStream 實時進度。
  • 純 JSON 交互,前後端解耦。

前端頁面 (index_ajax.html)

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>AJAX 上傳演示</title></head>
<body>
<h3>AJAX 無刷新上傳(帶進度)</h3>
<input type="file" id="fileInput">
<button id="btnUpload">上傳</button>
<progress id="progress" value="0" max="100" style="width:300px"></progress>
<div id="result"></div>

<script>
document.getElementById('btnUpload').onclick = async () => {
    const file = document.getElementById('fileInput').files[0];
    if (!file) return alert('請選擇文件');

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

    const xhr = new XMLHttpRequest();
    xhr.open('POST', 'upload_ajax.php', true);

    xhr.upload.onprogress = e => {
        if (e.lengthComputable) {
            document.getElementById('progress').value = (e.loaded / e.total) * 100;
        }
    };

    xhr.onload = () => {
        if (xhr.status === 200) {
            const res = JSON.parse(xhr.responseText);
            document.getElementById('result').textContent = res.success ? '成功:' + res.url : '失敗:' + res.error;
        } else {
            document.getElementById('result').textContent = 'HTTP ' + xhr.status;
        }
    };

    xhr.onerror = () => document.getElementById('result').textContent = '網絡錯誤';
    xhr.send(formData);
};
</script>
</body></html>

後端處理 (upload_ajax.php)

<?php
header('Content-Type: application/json; charset=utf-8');
$targetDir = __DIR__ . '/uploads/';
if (!is_dir($targetDir)) mkdir($targetDir, 0755, true);

$resp = ['success' => false];
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upfile'])) {
    $file = $_FILES['upfile'];
    if ($file['error'] === UPLOAD_ERR_OK) {
        $ext = pathinfo($file['name'], PATHINFO_EXTENSION);
        $newName = uniqid('img_') . '.' . $ext;
        $dest = $targetDir . $newName;
        if (move_uploaded_file($file['tmp_name'], $dest)) {
            $resp = ['success' => true, 'url' => '/uploads/' . $newName];
        } else {
            $resp['error'] = '移動文件失敗';
        }
    } else {
        $resp['error'] = '上傳錯誤碼:' . $file['error'];
    }
}
echo json_encode($resp, JSON_UNESCAPED_UNICODE);

Fetch 現代寫法(可選)

const res = await fetch('upload_ajax.php', {
    method: 'POST',
    body: formData
    // 不要手動設置 Content-Type,瀏覽器會自動帶上 boundary
});
const data = await res.json();

安全與工程化建議

  1. 文件類型白名單:同時驗證 $_FILES['upfile']['type']、擴展名、finfo_file 魔數。
  2. 文件大小限制:php.ini upload_max_filesize + 前端 file.size 雙重校驗。
  3. 存儲路徑隨機化:避免遍歷下載,生產環境建議對象存儲(OSS/S3)+ 簽名直傳。
  4. CSRF 保護:AJAX 方案在 Header 帶上 Token,iframe 方案在表單隱藏域帶 Token。
  5. 錯誤碼國際化:後端返回統一 {code, message, data} 結構。

選型決策表

場景推薦方案
須支持 IE8/9、老舊內網系統iframe
需要上傳進度條、大文件分片、斷點續傳AJAX + FormData / WebUploader / Uppy
現代項目、前後端分離、TypeScript/React/VueAJAX (Fetch) + 統一請求庫
快速原型、零依賴、內部工具iframe 可接受

總結

iframe 方案是「兼容性兜底」,AJAX + FormData 是「現代標準」。新項目默認選 AJAX,享受進度監控、類型安全、前後端解耦帶來的開發體驗提升;僅在不得不兼容古老環境時才退回 iframe。

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