string js = "";
Response.Write(string.Format(js, url));
缺點:被彈出視窗攔截器擋截、無法控制檔案名、無法驗證權限、大檔案記憶體壓力大。
protected void DownloadFile(string physicalPath)
{
// 1. 取得檔案資訊
FileInfo file = new FileInfo(physicalPath);
if (!file.Exists) { Response.StatusCode = 404; return; }
// 2. 設定回應標頭
Response.Clear();
Response.ClearHeaders();
Response.Buffer = false; // 關閉緩衝,直接串流輸出,大檔案不佔記憶體
Response.ContentType = "application/octet-stream"; // 通用二進位,強制下載
Response.AddHeader("Content-Disposition", $"attachment; filename=\"{HttpUtility.UrlEncode(file.Name, Encoding.UTF8)}\"");
Response.AddHeader("Content-Length", file.Length.ToString());
Response.AddHeader("Content-Transfer-Encoding", "binary");
// 3. 分塊讀取並輸出 (避免大檔案一次性載入記憶體)
const int bufferSize = 1024 * 64; // 64KB
byte[] buffer = new byte[bufferSize];
long sent = 0;
using (FileStream fs = new FileStream(physicalPath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
while (sent < file.Length && Response.IsClientConnected)
{
int read = fs.Read(buffer, 0, bufferSize);
if (read == 0) break;
Response.OutputStream.Write(buffer, 0, read);
Response.Flush(); // 立即發送給客戶端
sent += read;
}
}
// 4. 結束回應
Response.End(); // 或 HttpContext.Current.ApplicationInstance.CompleteRequest();
}
| 設定 | 用途 |
|---|---|
| Content-Disposition: attachment | 強制瀏覽器彈出「另存新檔」對話框,而非直接開啟 |
| filename 編碼 (UrlEncode + UTF-8) | 支援中文檔名,避免亂碼 (RFC 5987 進階編碼更佳) |
| Content-Length | 讓瀏覽器顯示下載進度、支援斷點續傳 (需配合 Range 請求) |
| Buffer = false + Flush | 串流模式,記憶體佔用恆定 (約 bufferSize),適合 GB 級大檔 |
| Response.IsClientConnected | 偵測使用者取消下載,提早釋放資源 |
| FileShare.Read | 允許同時多使用者下載同一檔案 |
// 在輸出前檢查 Request.Headers["Range"]
// 回應 206 Partial Content、Content-Range、Accept-Ranges: bytes
// 實作較複雜,建議使用成熟庫或 IIS 靜態檔案處理 (配合授權模組)