資料庫儲存影像、文件等二進位物件 (BLOB) 是常見需求。SQL Server 提供 IMAGE (舊版) 與 VARBINARY(MAX) (2005+) 資料型別。本文以 ASP.NET (C#) 為例,示範完整的上傳儲存、下載輸出流程,並說明現代最佳實踐 (Filestream/FileTable、雲端 Blob 儲存)。
CREATE TABLE dbo.ImageStore
(
ID INT IDENTITY(1,1) PRIMARY KEY,
ImgTitle NVARCHAR(100) NOT NULL,
ImgType NVARCHAR(100) NOT NULL, -- MIME 類型,如 image/jpeg, application/pdf
ImgData VARBINARY(MAX) NOT NULL -- SQL Server 2005+ 建議用 VARBINARY(MAX) 取代 IMAGE
);
注意:IMAGE 為舊版型別,未來版本將移除,新專案請使用 VARBINARY(MAX)。
<%@ Page Language="C#" %>
<form id="form1" runat="server" enctype="multipart/form-data">
標題: <asp:TextBox ID="txtTitle" runat="server" /><br />
檔案: <input type="file" id="File1" runat="server" /><br />
<asp:Button ID="btnUpload" runat="server" Text="上傳" OnClick="btnUpload_Click" />
</form>
protected void btnUpload_Click(object sender, EventArgs e)
{
if (File1.PostedFile == null || File1.PostedFile.ContentLength == 0) return;
Stream fs = File1.PostedFile.InputStream;
int len = File1.PostedFile.ContentLength;
string type = File1.PostedFile.ContentType;
string title = txtTitle.Text;
byte[] data = new byte[len];
fs.Read(data, 0, len);
string connStr = ConfigurationManager.ConnectionStrings["ImgDB"].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, 100).Value = title;
cmd.Parameters.Add("@Type", SqlDbType.NVarChar, 100).Value = type;
cmd.Parameters.Add("@Data", SqlDbType.VarBinary, -1).Value = data; // -1 = MAX
conn.Open();
cmd.ExecuteNonQuery();
}
}
關鍵點:
SqlParameter 參數化,防止 SQL Injection、自動處理二進位編碼。SqlDbType.VarBinary, -1 對應 VARBINARY(MAX)。using 確保連線/命令正確釋放。public partial class GetImage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
string id = Request.QueryString["id"];
if (string.IsNullOrEmpty(id)) return;
string connStr = ConfigurationManager.ConnectionStrings["ImgDB"].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())
{
Response.Clear();
Response.ContentType = dr.GetString(1); // ImgType
// 使用 GetBytes 串流輸出,避免大檔案記憶體壓力
long offset = 0;
byte[] buffer = new byte[8192];
long bytesRead;
while ((bytesRead = dr.GetBytes(0, offset, buffer, 0, buffer.Length)) > 0)
{
Response.OutputStream.Write(buffer, 0, (int)bytesRead);
offset += bytesRead;
}
Response.End();
}
}
}
}
}
呼叫方式:<img src="GetImage.aspx?id=123" />
| 方案 | 適用場景 | 優點 | 缺點 |
|---|---|---|---|
| VARBINARY(MAX) 資料庫內儲存 | 小檔案 (< 1MB)、交易一致性要求高、備份單一檔案 | ACID、備份簡單、權限隨資料庫 | 資料庫膨脹、備份/還原慢、記憶體壓力 |
| FILESTREAM (SQL Server 2008+) | 大檔案 (1MB~2GB)、Win32 流式存取、需交易一致性 | NTFS 流效能、不佔資料庫空間、支援備份 | 需啟用檔案群組、僅限本機/Windows 驗證 |
| FileTable (SQL Server 2012+) | 非結構化檔案目錄、需檔案系統 API (完整路徑、屬性) | 透過資料表/檔案系統雙重存取、全文檢索 | 版本要求高、管理複雜度較高 |
| Azure Blob / AWS S3 + 資料庫存 URL | 雲端原生、海量、CDN 分發、跨區域 | 無限擴展、成本低、全球分發、生命週期管理 | 交易一致性最終一致、需應用層協調 |
ContentType,需檢查檔案簽名 (Magic Number) 或用 System.Drawing.Image 驗證影像合法性。web.config 設定 <httpRuntime maxRequestLength="10240" /> (KB) + IIS 要求篩選限制。GetBytes / SqlFileStream 串流,避免一次性載入記憶體導致 OOM。Response.Cache.SetCacheability(HttpCacheability.Public); Response.Cache.SetMaxAge(TimeSpan.FromDays(30)); 瀏覽器/CDN 快取。