資料表設計
CREATE TABLE ImageStore (
ID INT IDENTITY(1,1) PRIMARY KEY,
ImgTitle NVARCHAR(50) NOT NULL, -- 使用者可讀標題
ImgType VARCHAR(50) NOT NULL, -- MIME 類型,如 image/jpeg
ImgData VARBINARY(MAX) NOT NULL -- 實際二進位內容 (SQL 2005+ 建議用 VARBINARY(MAX))
);
SQL 2000 請用 `IMAGE` 類型;SQL 2005+ 強烈建議改用 `VARBINARY(MAX)`,支援所有字串函數且無 2GB 限制問題。
上傳與儲存(ASP.NET C# 後端)
protected void btnUpload_Click(object sender, EventArgs e)
{
if (!FileUpload1.HasFile) return;
// 1. 讀取檔案流
Stream fs = FileUpload1.PostedFile.InputStream;
int len = FileUpload1.PostedFile.ContentLength;
string mime = FileUpload1.PostedFile.ContentType;
string title = txtTitle.Text;
byte[] buffer = new byte[len];
fs.Read(buffer, 0, len);
// 2. 參數化寫入 (防 SQL 注入、處理大物件)
string connStr = ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(
"INSERT INTO ImageStore (ImgTitle, ImgType, ImgData) VALUES (@Title, @Type, @Data)", conn))
{
cmd.Parameters.Add("@Title", SqlDbType.NVarChar, 50).Value = title;
cmd.Parameters.Add("@Type", SqlDbType.VarChar, 50).Value = mime;
cmd.Parameters.Add("@Data", SqlDbType.VarBinary, -1).Value = buffer; // -1 = MAX
conn.Open();
cmd.ExecuteNonQuery();
}
}
讀取並輸出至瀏覽器(通用處理器 .ashx 或頁面 Page_Load)
public void ProcessRequest(HttpContext context)
{
string id = context.Request.QueryString["id"];
if (string.IsNullOrEmpty(id)) return;
string connStr = ConfigurationManager.ConnectionStrings["MyDB"].ConnectionString;
using (SqlConnection conn = new SqlConnection(connStr))
using (SqlCommand cmd = new SqlCommand(
"SELECT ImgData, ImgType FROM ImageStore WHERE ID = @ID", conn))
{
cmd.Parameters.Add("@ID", SqlDbType.Int).Value = int.Parse(id);
conn.Open();
using (SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
if (dr.Read())
{
context.Response.ContentType = dr.GetString(1); // ImgType
// 使用 GetBytes 避免大物件一次載入記憶體
long offset = 0;
byte[] chunk = new byte[8192];
long bytesRead;
while ((bytesRead = dr.GetBytes(0, offset, chunk, 0, chunk.Length)) > 0)
{
context.Response.OutputStream.Write(chunk, 0, (int)bytesRead);
offset += bytesRead;
}
}
}
}
}
前端呼叫
<img src="GetImage.ashx?id=123" alt="動態圖片" />
效能與架構建議
- 檔案系統 vs 資料庫:小檔(<1MB)、需交易一致性、備份簡化 → 資料庫;大檔、高並發靜態存取 → 檔案系統 + 資料庫存路徑(FileTable/FILESTREAM 2008+ 兩全其美)。
- 串流讀寫:大物件務必使用 `CommandBehavior.SequentialAccess` + `GetBytes`/`GetChars` 分塊處理,避免 LOH 壓力與 OOM。
- 索引策略:二進位欄位不建索引;僅對 `ImgTitle`、`ImgType` 等查詢欄位建立索引。