絕不在 URL 直接暴露實體檔案路徑。改由資料庫儲存檔案相對路徑或 ID,下載頁面根據 ID 查詢路徑,再以 Response.BinaryWrite 或 Response.WriteFile 串流輸出。
// Download.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
string fileId = Request.QueryString["id"];
if (string.IsNullOrEmpty(fileId)) { Response.End(); return; }
// 1. 從資料庫取得相對路徑
string sql = "SELECT FileVirtualPath FROM Attachments WHERE FileID = @id";
string relPath = DbHelper.ExecuteScalar(sql, new SqlParameter("@id", fileId))?.ToString();
if (string.IsNullOrEmpty(relPath)) { Response.Write("檔案不存在"); Response.End(); return; }
// 2. 轉換為實體路徑
string fullPath = Server.MapPath(relPath);
if (!File.Exists(fullPath)) { Response.Write("實體檔案遺失"); Response.End(); return; }
// 3. 設定回應標頭
FileInfo fi = new FileInfo(fullPath);
Response.Clear();
Response.ClearHeaders();
Response.Buffer = false; // 大檔案建議 false,避免記憶體暴增
Response.ContentType = "application/octet-stream";
Response.AddHeader("Content-Disposition",
$"attachment; filename=\"{HttpUtility.UrlEncode(fi.Name, Encoding.UTF8)}\"");
Response.AddHeader("Content-Length", fi.Length.ToString());
// 4. 串流輸出
Response.WriteFile(fullPath);
Response.Flush();
Response.End();
}
<!--#include file="conn.asp" -->
<%
Response.Buffer = True
Response.Clear
Dim id, sql, rs, relPath, fullPath, fso, file, fileName, fileSize
id = Trim(Request.QueryString("id"))
If id = "" Then Response.Write("參數錯誤") : Response.End
sql = "SELECT FilePath FROM Attachments WHERE FileID = " & id
Set rs = Server.CreateObject("ADODB.Recordset")
rs.Open sql, conn, 1, 1
If rs.EOF Then Response.Write("找不到記錄") : Response.End
relPath = rs("FilePath")
rs.Close : Set rs = Nothing
fullPath = Server.MapPath(relPath)
Set fso = Server.CreateObject("Scripting.FileSystemObject")
If Not fso.FileExists(fullPath) Then Response.Write("檔案不存在") : Response.End
Set file = fso.GetFile(fullPath)
fileName = file.Name
fileSize = file.Size
Set file = Nothing : Set fso = Nothing
' 依副檔名決定 ContentType
Dim ext, contentType
ext = LCase(Right(fileName, 4))
Select Case ext
Case ".pdf" : contentType = "application/pdf"
Case ".doc" : contentType = "application/msword"
Case ".xls" : contentType = "application/vnd.ms-excel"
Case ".jpg", "jpeg" : contentType = "image/jpeg"
Case ".png" : contentType = "image/png"
Case ".zip" : contentType = "application/zip"
Case Else : contentType = "application/octet-stream"
End Select
Response.AddHeader "Content-Disposition", "attachment; filename=" & fileName
Response.AddHeader "Content-Length", fileSize
Response.ContentType = contentType
Response.Charset = "UTF-8"
' 使用 ADODB.Stream 讀取二進位輸出(支援大檔案)
Dim stream
Set stream = Server.CreateObject("ADODB.Stream")
stream.Open
stream.Type = 1 ' adTypeBinary
stream.LoadFromFile fullPath
Response.BinaryWrite stream.Read
stream.Close
Set stream = Nothing
Response.Flush
Response.End
%>
SqlParameter,ASP 確保 ID 為數字)。Server.MapPath 限制在網站根目錄下。HttpUtility.UrlEncode(Encoding.UTF8) 解決中文檔名下載亂碼。Response.Buffer = false + Response.WriteFile / ADODB.Stream 避免記憶體爆炸。需解析 Request.Headers["Range"],回傳 206 Partial Content、Content-Range、Accept-Ranges: bytes。建議使用成熟庫(如 FileResult 擴充或 RangeFileStreamResult)而非手寫。