版上的大家下午安,有個問題思考很久
搞不定![]()
我目前開發一個功能 一鍵下載附檔。
將網頁上的附檔,透過一個按鈕可以全部下載下來。
有用到AJAX服務,設定檔案名稱。 ajax_WmsAccessor.setAttachmentFileName
組成檔案URL後,執行下載檔案的功能函式openSoure(tSourceFileUrl)
似乎下載太快,三個檔案的名稱都一樣...檔案大小不一樣。
試過加入alert() 延緩處理的程序,
下載的檔案就完全正確。
請教高手們這個小問題,我可以如何處理,讓AJAX服務執行完 再跑後面的下載。
謝謝大家。
DWREngine.setAsync(false);
if (tResultArray.length>0)
{
for(var i=0;i<tResultArray.length;i++)
{
ajax_WmsAccessor.setAttachmentFileName(tResultArray[i][2]); //設定附檔名稱。
var tSourceFileUrl = "/NaNaWeb/DownloadFile/"+tResultArray[i][0]+";"+"?action=downloadDocument" + "&docFileName=" + encodeURIComponent(tResultArray[i][0]);
openSoure(tSourceFileUrl); //下載檔案。
alert(tSourceFileUrl);
}
}
}
//下載原始檔
function openSoure(pSourceFileUrl) {
var iframe = document.createElement('iframe');
iframe.src = pSourceFileUrl;
iframe.setAttribute("class", "download");
iframe.style.display = "none";
document.body.appendChild(iframe);
cleanDownload();
}
這是網頁上要下載的三個檔案:
將alert() 拿掉後,下載的檔案名稱都同一個....
若有alert()延緩處理時間,檔案下載都成功。
提供一個曾經的解法,讓您參考看看。
目的:加入alert() 延緩處理的程序
方向:讓系統等待個2秒就好了
Javascript我不熟,大致方向就是系統停個2秒鐘。
setTimeout(function() {
console.log("This message appears after 2 second.");
}, 2000);
ajax_WmsAccessor.setAttachmentFileName
如果只是為了設定附檔名稱,為什麼要用非同步的ajax?
如果一定要用,就先把附檔名稱都準備好(確定你要的名稱都ok了),再來進行下載。
這種一次下載多個檔案的功能我常做,常用做法都是用 Promise.all()。
簡單講一下做法,你可以透過 AI 問更詳細的資訊。
因為你現在截圖的畫面上有呈現所有檔案的名稱了,那我就假設你可以把所有檔名存成一個陣列,
然後把陣列每個元素做 promisify,這樣就生成一個多個 Promise 物件的陣列,
然後用 Promise.all() 一次 call 下載檔案的 API 即可。
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<meta charset="UTF-8">
<title>Promise.all 批量下載檔案</title>
</head>
<body>
<h1>批量下載檔案範例</h1>
<button id="downloadButton">開始下載選定的檔案</button>
<script>
// 預先準備的檔名列表
const fileNames = [
'document_A.pdf',
'image_B.jpg',
'data_C.xlsx'
];
// 模擬的 API 呼叫函式
// 實際應用中,你需要替換成你的後端 API 呼叫
/**
* 模擬從 API 下載一個檔案
* @param {string} fileName 要下載的檔案名稱
* @returns {Promise<{fileName: string, blob: Blob}>} 包含檔名和 Blob 資料的 Promise
*/
function downloadFileFromApi(fileName) {
console.log(`正在請求檔案: ${fileName}`);
// 實際應用中,這裡會是 fetch 或 XMLHttpRequest
return fetch(`/api/download?file=${fileName}`, {
method: 'GET',
// 這裡可以加上必要的 headers,例如授權 token
})
.then(response => {
// 檢查 HTTP 狀態碼
if (!response.ok) {
throw new Error(`API 請求失敗,狀態碼: ${response.status} (${response.statusText})`);
}
// 假設 API 返回的是檔案的 Blob 資料
return response.blob();
})
.then(blob => {
// 返回一個包含檔名和 Blob 資料的物件
return { fileName: fileName, blob: blob };
})
.catch(error => {
// 處理網路或 API 錯誤
console.error(`下載 ${fileName} 時發生錯誤:`, error);
// 即使失敗,也返回一個可以被 Promise.all 處理的結果
// 可以在這裡返回一個特定的失敗標記,如果 Promise.all 必須全部成功才繼續
// 如果需要「任一失敗則整體失敗」,則直接讓錯誤繼續拋出
throw error; // 讓 Promise.all 進入 catch 區塊
});
}
// 建立下載連結並觸發下載的輔助函式
/**
* 使用 Blob 資料在瀏覽器中建立下載連結並觸發下載
* @param {string} fileName 下載的檔案名稱
* @param {Blob} blob 檔案的 Blob 資料
*/
function triggerDownload(fileName, blob) {
// 創建一個指向 Blob 資料的 URL
const url = window.URL.createObjectURL(blob);
// 創建一個隱藏的 <a> 元素
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
// 設置下載的檔名
a.download = fileName;
// 將元素添加到 DOM 並點擊它以觸發下載
document.body.appendChild(a);
a.click();
// 清理:釋放創建的 URL 並移除元素
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}
// 處理批量下載的主函式
async function batchDownloadFiles() {
const button = document.getElementById('downloadButton');
button.disabled = true; // 下載期間禁用按鈕
button.textContent = '正在下載...';
try {
// 1. 建立 Promise 陣列
// 對每個檔名呼叫 downloadFileFromApi,並將返回的 Promise 收集起來
const downloadPromises = fileNames.map(fileName => downloadFileFromApi(fileName));
// 2. 使用 Promise.all 併發執行所有下載 Promise
// 它會等待所有 Promise 都成功 resolve
const results = await Promise.all(downloadPromises);
// 3. 處理下載結果
console.log('所有檔案下載完成,準備觸發下載。');
// results 是一個陣列,包含每個檔案的 {fileName, blob} 物件
results.forEach(result => {
// 觸發每個檔案的下載
triggerDownload(result.fileName, result.blob);
});
alert(`成功下載 ${results.length} 個檔案!請檢查您的下載資料夾。`);
} catch (error) {
// 如果 Promise.all 中任一個 Promise 發生 rejected,就會進入這裡
console.error('批量下載過程中發生錯誤:', error);
alert(`批量下載失敗。請查看控制台瞭解詳情。錯誤: ${error.message}`);
} finally {
// 無論成功或失敗,最後都恢復按鈕狀態
button.disabled = false;
button.textContent = '開始下載選定的檔案';
}
}
// 為按鈕添加事件監聽器
document.getElementById('downloadButton').addEventListener('click', batchDownloadFiles);
// 由於瀏覽器安全限制,直接運行這個程式碼時,
// 模擬的 /api/download 請求會失敗(因為沒有這個端點),
// 如果想在本地測試下載流程,可以將 downloadFileFromApi 替換為一個返回假 Blob 的函式。
// --- 替換為模擬成功的函式(用於本地測試下載流程) ---
/*
function downloadFileFromApi(fileName) {
console.log(`正在模擬請求檔案: ${fileName}`);
return new Promise(resolve => {
setTimeout(() => { // 模擬網路延遲
// 創建一個假的 Blob 資料
const mockContent = `這是 ${fileName} 檔案的內容。`;
const mockBlob = new Blob([mockContent], { type: 'text/plain' });
resolve({ fileName: fileName, blob: mockBlob });
}, 500 + Math.random() * 500);
});
}
*/
// ---------------------------------------------------
</script>
</body>
</html>