| 方案 | 核心原理 | 瀏覽器兼容 | 交互體驗 | 進度控制 | 實現難度 |
|---|---|---|---|---|---|
| 第三方插件 | Flash/ActiveX 底層 API | 需安裝插件 | 優秀 | 完全可控 | 高(底層開發) |
| 隱藏 iframe | form target 指向 iframe,頁面不跳轉 | 極佳(IE6+) | 一般(無進度) | 不可控 | 低 |
| XHR Level 2 | FormData + XMLHttpRequest 原生異步 | IE10+、現代瀏覽器 | 極佳(實時進度、可取消) | 完全可控 | 中 |
利用插件繞過瀏覽器沙箱,直接訪問文件系統、控制上傳流程。優勢:大文件分片、斷點續傳、多線程並發、圖片預覽壓縮均可在客戶端完成。劣勢:用戶需安裝插件、移動端不支持、安全性受質疑、維護成本高。現已淘汰,不再展開。
<form target="iframeName"> 目標指向隱藏 <iframe name="iframeName">;<script>window.parent.callback(data)</script> 調用主頁面 JS 通知結果。<iframe name="frm" style="display:none;"></iframe>
<form action="/upload2" method="post" enctype="multipart/form-data" target="frm" onsubmit="loading(true);">
<input type="file" name="myfile">
<input type="submit" value="異步上傳">
<span id="uptxt">正在上傳...</span>
</form>
<div id="flist"></div>
<script>
function uploadFinished(fileName) {
addToFlist(fileName);
loading(false);
}
function addToFlist(fname) {
$("#flist").append("<p id='" + fname + "'>" + fname + " <button onclick='delFile(\"" + fname + "\")'>刪除</button></p>");
}
function loading(show) {
$("#uptxt").toggle(show);
}
</script>
// Node.js/Express 示例
router.post('/upload2', multipartMiddleware, (req, res) => {
const fname = path.basename(req.files.myfile.path);
setTimeout(() => { // 模擬處理延遲
res.send(`<script>window.parent.uploadFinished('${fname}');</script>`);
}, 3000);
});
document.domain。FormData:鍵值對構建 multipart/form-data,append('file', fileInput.files[0]);XMLHttpRequest.upload.onprogress:上傳進度事件(loaded/total);xhr.onloadstart/load/error/abort/loadend:完整生命週期事件;xhr.abort():取消上傳。<input type="file" id="myfile">
<button onclick="upload()">異步上傳</button>
<span id="upprog"></span>
<button id="stopbtn" style="display:none">停止上傳</button>
<div id="flist"></div>
<script>
function upload() {
const fd = new FormData();
fd.append("myfile", $("#myfile")[0].files[0]);
const xhr = new XMLHttpRequest();
// 進度
xhr.upload.onprogress = e => {
if (e.lengthComputable) {
const pct = Math.round(e.loaded * 100 / e.total);
$("#upprog").text(pct + '%');
}
};
// 開始
xhr.onloadstart = () => {
$("#upprog").text('開始上傳');
$("#stopbtn").show().one('click', () => xhr.abort());
loading(true);
};
// 成功
xhr.onload = () => {
$("#upprog").text('上傳成功');
const ret = JSON.parse(xhr.responseText);
addToFlist(ret.fname);
};
// 失敗/取消/結束
xhr.onerror = () => $("#upprog").text('錯誤');
xhr.onabort = () => $("#upprog").text('已取消');
xhr.onloadend = () => loading(false);
xhr.open('POST', '/upload3', true);
xhr.send(fd);
}
function loading(show) {
$("#uptxt, #stopbtn").toggle(show);
}
router.post('/upload3', multipartMiddleware, (req, res) => {
const fname = path.basename(req.files.myfile.path);
res.json({ fname }); // 返回文件名供前端展示
});
loadstart:接收到首字節;progress:持續觸發,計算進度;error:網絡/服務端錯誤;abort:調用 abort() 取消;load:完整接收響應;loadend:無論成敗最終觸發。| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 企業內網、老舊瀏覽器(IE8/9) | 隱藏 iframe | 零依賴、兼容性最好 |
| 現代 Web 應用、移動端 H5 | XHR Level 2 / Fetch API | 原生支持、體驗最佳、可進度/取消 |
| 大文件、斷點續傳、圖片客戶端壓縮 | WebUploader / Uppy / 自研分片 | 基於 XHR2/Fetch 封裝,功能豐富 |
| 歷史系統維護、最小改動 | iframe 方案 | 不改動服務端接口,快速接入 |
從「插件霸權」到「iframe 變通」再到「XHR2 原生」,文件上傳技術演進折射出 Web 標準的成熟。現代項目首選 XHR Level 2(或 Fetch API),配合 FormData 實現原生異步、進度可視、可取消的優秀體驗。舊系統維護可用 iframe 方案過渡。理解 iframe 方案的「目標導向 + 跨幀通信」思想,更能啟發解決其他跨域、無刷新場景的靈感。