在企業內部系統或檔案管理平台中,常需要讓使用者瀏覽伺服器特定資料夾下的檔案並提供下載。本文將完整示範如何使用 ASP.NET 讀取伺服器目錄檔案、綁定至 Repeater 控制項顯示,並實作安全的檔案下載功能,包含中文檔名編碼處理與錯誤攔截機制。
~/uploads/)下的所有檔案Repeater 控制項呈現CommandName 識別動作並傳遞檔名參數在 Page_Load 或專門的資料繫結方法中,使用 System.IO 命名空間下的 DirectoryInfo 與 FileInfo 類別掃描檔案:
string dirPath = HttpContext.Current.Server.MapPath("~/uploads/");
if (Directory.Exists(dirPath))
{
DirectoryInfo dir = new DirectoryInfo(dirPath);
// 取得所有檔案,可依需求過濾副檔名,如 "*.pdf"
FileInfo[] files = dir.GetFiles("*.*");
// 建立臨時 DataTable 作為 Repeater 資料來源
DataTable dt = new DataTable();
dt.Columns.Add("FileName");
foreach (FileInfo fileInfo in files)
{
DataRow dr = dt.NewRow();
dr["FileName"] = fileInfo.Name;
dt.Rows.Add(dr);
}
Repeater1.DataSource = dt;
Repeater1.DataBind();
}
Repeater 前端範例(.aspx):
<asp:Repeater ID="Repeater1" runat="server" OnItemCommand="Repeater1_ItemCommand">
<ItemTemplate>
<div class="file-item">
<span><%# Eval("FileName") %></span>
<asp:LinkButton ID="lnkDownload" runat="server"
CommandName="down"
CommandArgument='<%# Eval("FileName") %>'
Text="下載" />
</div>
</ItemTemplate>
</asp:Repeater>
在 Repeater1_ItemCommand 事件中處理下載請求,核心在於正確設定 Response 標頭:
protected void Repeater1_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "down")
{
try
{
string fileName = e.CommandArgument.ToString();
string virtualPath = "~/uploads/" + fileName;
string physicalPath = Server.MapPath(virtualPath);
// 檢查檔案是否存在
FileInfo file = new FileInfo(physicalPath);
if (!file.Exists)
{
Response.Write("<script>alert('找不到下載的檔案');</script>");
return;
}
// 清除輸出緩衝區
Response.Clear();
Response.ClearHeaders();
Response.ClearContent();
// 設定 Content-Type 為二進位串流,強制瀏覽器彈出下載對話框
Response.ContentType = "application/octet-stream";
// 關鍵:使用 UrlEncode 處理中文檔名,避免瀏覽器顯示亂碼
// 必須指定 Encoding.UTF8 確保各瀏覽器相容
string encodedFileName = HttpUtility.UrlEncode(file.Name, System.Text.Encoding.UTF8);
Response.AddHeader("Content-Disposition", "attachment; filename=" + encodedFileName);
// 告知瀏覽器檔案大小,支援下載進度顯示
Response.AddHeader("Content-Length", file.Length.ToString());
// 將檔案直接寫入輸出串流(適合中小型檔案)
Response.WriteFile(physicalPath);
Response.Flush();
Response.End();
}
catch (Exception ex)
{
// 記錄錯誤日誌(建議使用 log4net 或 NLog)
// Log.Error("檔案下載失敗", ex);
Response.Write("<script>alert('下載過程發生錯誤');</script>");
}
}
}
直接在 Content-Disposition 中使用中文檔名會導致 IE、Edge、Chrome 等瀏覽器顯示亂碼或截斷。標準做法是使用 HttpUtility.UrlEncode 搭配 UTF-8 編碼:
string encodedFileName = HttpUtility.UrlEncode(file.Name, Encoding.UTF8);
Response.AddHeader("Content-Disposition", $"attachment; filename={encodedFileName}");
進階做法可參考 RFC 5987,同時提供 filename* 參數以支援現代瀏覽器:
Response.AddHeader("Content-Disposition",
$"attachment; filename={encodedFileName}; filename*=UTF-8''{encodedFileName}");
Response.WriteFile:將檔案讀入記憶體再輸出,適合小檔案(< 100MB),大檔案易造成記憶體壓力。Response.TransmitFile:使用 Windows 核心模式傳輸,不佔用 ASP.NET 工作執行緒記憶體,支援超大檔案(GB 級),建議生產環境優先使用。若系統需支援大檔案(> 500MB),建議改用分塊傳輸:
Response.BufferOutput = false 關閉緩衝FileStream 讀取檔案區塊,搭配 Response.OutputStream.Write 輸出Response.Flush()Response.IsClientConnected,若用戶中斷連線則提早終止釋放資源詳細大檔案下載實作請參考本系列下一篇:ASP.NET 大檔案分塊下載最佳實踐。
Path.GetFileName 只取檔名,並搭配 Server.MapPath 限定在特定資料夾下。[Authorize] 或自訂權限檢查)。| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| 下載檔案為 0 bytes 或損壞 | Response 輸出前有其他內容(如空白行、HTML) | 下載前呼叫 Response.Clear()、Response.ClearHeaders()、Response.ClearContent() |
| 中文檔名亂碼 | 未使用 UrlEncode 或編碼不一致 | 統一使用 HttpUtility.UrlEncode(filename, Encoding.UTF8) |
| 大檔案下載中斷 / 記憶體爆增 | 使用 WriteFile 讀入完整檔案 | 改用 TransmitFile 或分塊串流輸出 |
| UpdatePanel 非同步下載失效 | 檔案下載需完整 Postback,不支援 AJAX 部分更新 | 將下載按鈕設為 PostBackTrigger 或置於 UpdatePanel 外 |
本文示範了 ASP.NET 實作伺服器目錄檔案列表與下載的標準流程:DirectoryInfo 掃描 → DataTable 繫結 → Repeater 呈現 → ItemCommand 處理下載 → Response 標頭設定 → WriteFile/TransmitFile 輸出。掌握中文檔名編碼、大檔案傳輸策略與安全性防護,即可建構穩健的檔案下載功能。實務上建議封裝為共用的 FileHelper.DownloadFile() 靜態方法,減少重複代碼並統一錯誤處理邏輯。