好的,這個情境非常典型,因為 Return FileResult 會直接將檔案串流 (stream) 回傳給瀏覽器,前端的 JavaScript 無法直接監測到下載的「完成」事件。 這是一個瀏覽器機制的限制,但我們可以用一個非常經典且可靠的技巧來完美解決它。 狠|問題穿透:為何 JS 無法監測? * 單向的 HTTP 請求:您點擊按鈕,瀏覽器向伺服器發送一個請求。 * 伺服器的回應:Controller 處理完後,回傳的不是一般的 HTML 或 JSON,而是一個帶有特殊標頭 (Header) 的檔案串流,告訴瀏覽器:「這是一個檔案,請觸發下載」。 * 瀏覽器接管:瀏覽器看到這個特殊標頭後,就完全接管了下載過程。您看到的下載進度條、存檔對話框,都是瀏覽器自身的功能。 * JS 的失聯:此時,原本頁面上的 JavaScript 就像斷了線的風箏,它只知道請求已發出,但對於瀏覽器後續的下載行為(何時開始、進度如何、何時完成)一無所知。AJAX 的 success 或 error 回呼在這裡完全無效。 解決思路:Cookie 令牌 (Token) 技巧 我們需要一個後門,讓伺服器在完成工作後,能悄悄地通知前端。Cookie 就是最佳的後門。 * 前端:點擊按鈕時,產生一個獨一無二的「令牌」(Token),並告訴後端:「我要下載了,這是我的令牌」。同時,前端開始不斷檢查瀏覽器是否出現了一個帶有這個令牌的 Cookie。 * 後端:收到請求和令牌,開始忙碌地產生 Excel。在產生完畢、Return File 之前的最後一刻,將收到的那個令牌存入一個 Cookie 並回傳。 * 前端:在不斷的檢查中,終於發現了那個帶有正確令牌的 Cookie,於是它知道:「後端任務完成了!」便關閉轉圈圈動畫,並跳出成功訊息。 準|精準步驟與程式碼 步驟 1:修改 Controller (VB.NET) 在您的 ExportExcel Action 中,增加接收令牌並設定 Cookie 的邏輯。 Function ExportExcel(ByVal downloadToken As String) As ActionResult Try ' 1. 顯示轉圈圈 (前端已做) ' 2. 執行您原本耗時的 Excel 產生邏輯 ' ... (例如:查詢資料庫、寫入 MemoryStream) Dim ms As New MemoryStream() ' ... (您將資料寫入 ms 的程式碼) ... ' 3. 【關鍵步驟】在回傳 File 前,將收到的 token 寫入 Cookie If Not String.IsNullOrEmpty(downloadToken) Then Response.SetCookie(New HttpCookie("downloadToken", downloadToken)) End If ' 4. 正常回傳您的 FileResult Return File(ms.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Report.xlsx") Catch ex As Exception ' 處理例外情況(可選) ' 如果出錯,也可以設定一個錯誤的 cookie 來通知前端 Response.SetCookie(New HttpCookie("downloadToken", "error")) ' 可以返回一個錯誤頁面或 JSON Return Content("發生錯誤:" & ex.Message) End Try End Function 步驟 2:修改前端 View 與 JavaScript 我們將使用 JavaScript 來觸發下載、顯示 swal 動畫,並監控 Cookie。 View (.vbhtml) <button type="button" id="export-excel-btn" class="btn btn-success">匯出 Excel</button> JavaScript (@Section Scripts) @Section Scripts <script> // 輔助函式:用來讀取 Cookie function getCookie(name) { var value = "; " + document.cookie; var parts = value.split("; " + name + "="); if (parts.length == 2) return parts.pop().split(";").shift(); } document.getElementById('export-excel-btn').addEventListener('click', function () { // 1. 顯示轉圈圈的 swal (v1 語法) // 您可以找一個 loading.gif 的圖片網址來顯示動畫 swal({ title: "處理中...", text: "正在產生 Excel 檔案,請稍候。\n畫面不會跳轉,請勿關閉。", // imageUrl: "https://.../path/to/your/loading.gif", // 轉圈圈的 GIF showConfirmButton: false, // 隱藏確認按鈕 allowOutsideClick: false // 防止點擊外部關閉 }); // 2. 產生一個獨一無二的令牌 (用當前時間毫秒數即可) var token = new Date().getTime(); var downloadUrl = '@Url.Action("ExportExcel", "您的Controller名稱")'; // 3. 使用一個隱藏的 form 來觸發下載 (這是最可靠的方法) var form = document.createElement("form"); form.setAttribute("method", "post"); form.setAttribute("action", downloadUrl); var tokenInput = document.createElement("input"); tokenInput.setAttribute("type", "hidden"); tokenInput.setAttribute("name", "downloadToken"); tokenInput.setAttribute("value", token); form.appendChild(tokenInput); document.body.appendChild(form); form.submit(); document.body.removeChild(form); // 4. 開始輪詢檢查 Cookie 是否存在 var fileDownloadCheckTimer = window.setInterval(function () { var cookieValue = getCookie("downloadToken"); if (cookieValue == token) { // 如果 cookie 的值等於我們發送的 token window.clearInterval(fileDownloadCheckTimer); // 停止輪詢 // 清除 cookie (好習慣) document.cookie = "downloadToken=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"; // 關閉轉圈圈動畫,並顯示成功訊息 swal("完成!", "Excel 檔案已成功匯出。", "success"); } }, 1000); // 每 1000 毫秒 (1秒) 檢查一次 }); </script> End Section 快|總結 * 前端:點擊按鈕 -> 跳出 swal 轉圈動畫 -> 產生一個 token -> 透過隱藏 form 提交 token 到後端 -> 開始每秒檢查一次 document.cookie。 * 後端:接收到請求 -> 執行耗時的 Excel 產生 -> Return File 之前,把收到的 token 寫入 Response.SetCookie。 * 前端:輪詢檢查發現 Cookie 出現且 token 值相符 -> 停止輪詢 -> 關閉 swal 動畫 -> 跳出成功訊息 swal。 這個方法穩定、可靠,且不受瀏覽器差異影響,是處理此類問題的最佳實踐。
留言
留言分享你的想法!
Pocheng Chiu的沙龍
0會員
16內容數
Pocheng Chiu的沙龍的其他內容
2025/09/16
好的,了解了。這個情境非常清晰:「建立」與「讀取」是兩個獨立的步驟。
你既然已經成功建立了檔案,現在要讀取它,那麼 FileMode.Open 就是完全正確的選擇。如果在 File.Open 這一步就拋出 InvalidOperationException,問題點幾乎可以鎖定在資源未被釋放。
核心問
2025/09/16
好的,了解了。這個情境非常清晰:「建立」與「讀取」是兩個獨立的步驟。
你既然已經成功建立了檔案,現在要讀取它,那麼 FileMode.Open 就是完全正確的選擇。如果在 File.Open 這一步就拋出 InvalidOperationException,問題點幾乎可以鎖定在資源未被釋放。
核心問
2025/09/09
好的,這是一個非常常見且重要的優化場景。一個清晰、整齊的 Modal 介面能大大提升使用者體驗。
讓我為你提供一個結構清晰、易於理解且美觀的解決方案。我們將運用 Bootstrap 的網格系統 (Grid System) 和表單元件 (Form Controls) 來達成這個目標。
優化核心思路
2025/09/09
好的,這是一個非常常見且重要的優化場景。一個清晰、整齊的 Modal 介面能大大提升使用者體驗。
讓我為你提供一個結構清晰、易於理解且美觀的解決方案。我們將運用 Bootstrap 的網格系統 (Grid System) 和表單元件 (Form Controls) 來達成這個目標。
優化核心思路
2025/09/05
了解 🙌
你希望 預設用 SetCellValue(Double) 寫入數字,但又要能處理「可能為空白」的情況。
重點在於:空白不要強制變成 0(否則格式會顯示成 -),而是真的留空。
---
✅ 寫法範例
Public Sub SetNumberCell(cell As ICell, s
2025/09/05
了解 🙌
你希望 預設用 SetCellValue(Double) 寫入數字,但又要能處理「可能為空白」的情況。
重點在於:空白不要強制變成 0(否則格式會顯示成 -),而是真的留空。
---
✅ 寫法範例
Public Sub SetNumberCell(cell As ICell, s
你可能也想看














※ 原本狀態:伺服器渲染
這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。
伺服器渲染流程:
瀏覽器針對特定網址送出請求。
路由器解析請求後,轉接給對應的 controller。
controller 按照要求,透過

※ 原本狀態:伺服器渲染
這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。
伺服器渲染流程:
瀏覽器針對特定網址送出請求。
路由器解析請求後,轉接給對應的 controller。
controller 按照要求,透過

分享一個有趣的套件,名為 await-to-js。
可以讓 Promise 與 await 的寫法更簡潔。

分享一個有趣的套件,名為 await-to-js。
可以讓 Promise 與 await 的寫法更簡潔。

標示全部為已讀失效
最近發現留言系統中,"標示全部為已讀"的速度明顯變慢,甚至有時會失效。許多使用者都報告遇到了相同的問題。這實際上是程式設計中一個常見的漏洞。系統沒有充分考慮到整體容量問題與效能,才導致了這樣的情況。(實際原因待查,此處僅為一般解說),當系統開始顯示緩慢或出現其他問題時,通常

標示全部為已讀失效
最近發現留言系統中,"標示全部為已讀"的速度明顯變慢,甚至有時會失效。許多使用者都報告遇到了相同的問題。這實際上是程式設計中一個常見的漏洞。系統沒有充分考慮到整體容量問題與效能,才導致了這樣的情況。(實際原因待查,此處僅為一般解說),當系統開始顯示緩慢或出現其他問題時,通常

在專案中與廠商測試API回傳的json字串出現無法解析的狀況,記錄發現過程與解決的紀錄,提供程式面和檔案面的解決方法。

在專案中與廠商測試API回傳的json字串出現無法解析的狀況,記錄發現過程與解決的紀錄,提供程式面和檔案面的解決方法。
CSR(Client Side Rendering)是一種將渲染資料的過程交由瀏覽器端處理的方法。
CSR(Client Side Rendering)是一種將渲染資料的過程交由瀏覽器端處理的方法。

在開發前後端分離架構時,使用兩個不同網域所遇到跨域請求問題。特別是在POST請求時行為差異大,揭示了「簡單請求」與「預檢請求」的關鍵差異。簡單請求不需預檢,但application/json會觸發預檢請求,需透過特定設定解決。分享這篇文章希望幫助開發者有效處理跨域問題。

在開發前後端分離架構時,使用兩個不同網域所遇到跨域請求問題。特別是在POST請求時行為差異大,揭示了「簡單請求」與「預檢請求」的關鍵差異。簡單請求不需預檢,但application/json會觸發預檢請求,需透過特定設定解決。分享這篇文章希望幫助開發者有效處理跨域問題。

資料庫之備份工作大都是自動執行,但是執行結果是否成功,需要安排人員去檢查,有時疏忽忘記確認作業,致備份工作失敗仍不知道,等到有一天需要回復舊有資料的場合時,才發現找不到過去某段期間的備份資料,造成無法彌補之後果。
2. 改善:
2.1 設計一執行檔,功能為打開備

資料庫之備份工作大都是自動執行,但是執行結果是否成功,需要安排人員去檢查,有時疏忽忘記確認作業,致備份工作失敗仍不知道,等到有一天需要回復舊有資料的場合時,才發現找不到過去某段期間的備份資料,造成無法彌補之後果。
2. 改善:
2.1 設計一執行檔,功能為打開備