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

Ajax 文件下載最佳實踐:動態 Form 提交替代方案

     編輯:更多程式設計知識

瀏覽器安全策略禁止 AJAX 直接觸發文件下載(無法訪問響應流保存為文件)。標準解法是動態創建隱藏 `

` 提交,模擬同步導航請求。本文提供生產級封裝代碼。

核心原理

  • AJAX (`XMLHttpRequest`/`fetch`) 僅能獲取響應體數據,無法直接喚起「另存為」對話框
  • `` 提交可觸發瀏覽器原生下載行為
  • 動態創建、提交、移除 `` 實現無感知下載

通用下載工具函數


/**
 * 通過動態 Form 實現文件下載
 * @param {string} url - 下載接口地址
 * @param {Object} params - 表單參數 { key: value }
 * @param {string} [method='POST'] - HTTP 方法
 * @param {string} [target='_self'] - 目標窗口
 */
function downloadByForm(url, params, method = 'POST', target = '_self') {
    const form = document.createElement('form');
    form.style.display = 'none';
    form.method = method;
    form.action = url;
    form.target = target;  // '_blank' 新標籤頁,'_self' 當前頁
    
    // 添加參數
    Object.entries(params).forEach(([key, value]) => {
        const input = document.createElement('input');
        input.type = 'hidden';
        input.name = key;
        input.value = typeof value === 'string' ? value : JSON.stringify(value);
        form.appendChild(input);
    });
    
    document.body.appendChild(form);
    form.submit();
    document.body.removeChild(form);  // 提交後立即移除
}

// 使用示例
$('#btnExport').click(function() {
    downloadByForm('/api/export', { 
        type: 'excel', 
        fileId: currentFileId,
        filters: JSON.stringify(currentFilters)
    });
});

jQuery 版本(兼容舊項目)


$('#btnExport').click(function() {
    var form = $('').attr({
        method: 'POST',
        action: 'AjaxPost.aspx',
        target: '_blank',  // 新窗口下載,不阻塞當前頁
        style: 'display:none'
    });
    
    $('').attr({ type: 'hidden', name: 'type', value: 'Export' }).appendTo(form);
    $('').attr({ type: 'hidden', name: 'fileId', value: fileId }).appendTo(form);
    
    $('body').append(form);
    form.submit();
    form.remove();  // 關鍵:防止重複提交、DOM 泄漏
});

進階場景處理

場景方案代碼要點
大文件下載進度顯示Form 提交無法監聽進度改用 `fetch` + `ReadableStream` + `Content-Length` 計算進度,最後 `blob` -> `a[download]` 保存
需攜帶 Cookie/TokenForm 自動攜帶同域 Cookie跨域需設置 `form.target='_blank'` + 後端 CORS `Access-Control-Allow-Credentials: true`
防止重複點擊提交後按鈕禁用`btn.prop('disabled', true); setTimeout(() => btn.prop('disabled', false), 3000);`
參數過長超過 URL 限制POST Form 無長度限制大量數據建議先生成下載 Token,再用 Token 下載
IE 兼容性IE 不支持 `a[download]`Form 方案完美兼容 IE6+

現代替代:Fetch + Blob + Object URL (不支持流式大文件)


async function downloadByFetch(url, params) {
    const response = await fetch(url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(params),
        credentials: 'include'  // 攜帶 Cookie
    });
    
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    
    const blob = await response.blob();
    const filename = response.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1] || 'download';
    
    const a = document.createElement('a');
    a.href = URL.createObjectURL(blob);
    a.download = filename;
    a.style.display = 'none';
    document.body.appendChild(a);
    a.click();
    URL.revokeObjectURL(a.href);  // 釋放 Blob URL
    document.body.removeChild(a);
}

限制:需整個文件載入內存,不適用 >100MB 文件。大文件仍需 Form/iframe 或 Service Worker 流式寫入。

後端響應頭規範


// ASP.NET Core 示例
[HttpPost("export")]
public IActionResult Export([FromBody] ExportRequest req) {
    var stream = GenerateExcelStream(req);
    return File(stream, 
        "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
        $"report_{DateTime.Now:yyyyMMdd}.xlsx");
}

// 關鍵響應頭
Content-Type: application/octet-stream (或具體 MIME)
Content-Disposition: attachment; filename="report.xlsx"
Content-Length: 123456  (可選,啟用進度條)
Cache-Control: no-cache, no-store, must-revalidate

總結

動態 Form 提交是 AJAX 文件下載的通用、兼容、零依賴標準方案。封裝 `downloadByForm(url, params)` 統一全項目調用。現代項目小文件可用 `fetch + Blob + a[download]` 體驗更佳(可自定義文件名、無彈窗阻攔)。大文件、進度顯示、斷點續傳需引入專業下載庫或 Service Worker 方案。

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