在資料庫中儲存圖片、文件等二進位資料是常見需求。SQL Server 2008 引入 FILESTREAM 與 `VARBINARY(MAX)`,提供比舊版 `IMAGE` 型別更強大、效能更好的解決方案。
| 方案 | 適用版本 | 優點 | 缺點 | 建議場景 |
|---|---|---|---|---|
| VARBINARY(MAX) | 2005+ | ACID 交易一致性、備份還原單一檔案、T-SQL 直接操作 | 資料庫膨脹、大檔案效能下降、記錄檔壓力大 | < 1MB 小檔案、需交易一致性、檔案數量中等 |
| FILESTREAM | 2008+ | 檔案系統效能、不佔資料庫空間、支援大於 2GB、Windows 流式存取 | 需啟用檔案群組、備份較複雜、不支援鏡像/AlwaysOn 部分功能 | > 1MB 大檔案、大量 BLOB、需串流存取 |
| FileTable | 2012+ | FILESTREAM 延伸、非交易式 Windows API 存取、目錄結構映射 | 需 2012+、安全性模型不同 | 應用需直接用檔案總管/Office 存取 |
| 外部儲存 (路徑) | 所有版本 | 資料庫輕量、檔案系統原生效能、雲端儲存整合易 | 交易不一致風險、備份分離、路徑遷移麻煩 | 超大檔案、雲端架構、高並發靜態資源 |
CREATE TABLE product_images
(
image_id INT IDENTITY PRIMARY KEY,
product_id INT NOT NULL REFERENCES products(id),
file_name NVARCHAR(255) NOT NULL,
content_type NVARCHAR(50) NOT NULL, -- image/jpeg, image/png
file_size BIGINT NOT NULL,
image_data VARBINARY(MAX) NOT NULL,
created_at DATETIME DEFAULT GETDATE()
)
-- 索引優化查詢
CREATE INDEX IX_product_images_product ON product_images(product_id)
-- 1. 使用 OPENROWSET (僅限伺服器端檔案)
INSERT INTO product_images (product_id, file_name, content_type, file_size, image_data)
SELECT 1, 'photo.jpg', 'image/jpeg',
(SELECT LEN(BulkColumn) FROM OPENROWSET(BULK 'C:\Images\photo.jpg', SINGLE_BLOB) AS x),
BulkColumn
FROM OPENROWSET(BULK 'C:\Images\photo.jpg', SINGLE_BLOB) AS img
-- 2. 應用層參數化插入 (C# 範例)
/*
using (var cmd = new SqlCommand(
"INSERT INTO product_images (product_id, file_name, content_type, file_size, image_data) VALUES (@pid, @fn, @ct, @fs, @data)", conn))
{
cmd.Parameters.Add("@pid", SqlDbType.Int).Value = 1;
cmd.Parameters.Add("@fn", SqlDbType.NVarChar).Value = "photo.jpg";
cmd.Parameters.Add("@ct", SqlDbType.NVarChar).Value = "image/jpeg";
cmd.Parameters.Add("@fs", SqlDbType.BigInt).Value = fileBytes.Length;
cmd.Parameters.Add("@data", SqlDbType.VarBinary).Value = fileBytes;
cmd.ExecuteNonQuery();
}
*/
-- T-SQL 讀取 (應用層再輸出至 Response/BinaryWrite) SELECT file_name, content_type, image_data FROM product_images WHERE image_id = 1 -- 大檔案分塊讀取 (避免記憶體壓力) SELECT SUBSTRING(image_data, @offset, @chunk_size) FROM product_images WHERE image_id = 1
SQL Server Configuration Manager → 服務屬性 → FILESTREAM 頁籤 → 勾選所有選項
ALTER DATABASE [MyDB] ADD FILEGROUP fg_filestream CONTAINS FILESTREAM ALTER DATABASE [MyDB] ADD FILE (NAME = fs_data, FILENAME = 'D:\FSData') TO FILEGROUP fg_filestream
CREATE TABLE document_store
(
doc_id UNIQUEIDENTIFIER ROWGUIDCOL NOT NULL UNIQUE DEFAULT NEWID(),
file_name NVARCHAR(255),
file_extension NVARCHAR(10),
doc_content VARBINARY(MAX) FILESTREAM NULL,
created_at DATETIME DEFAULT GETDATE()
)
FILESTREAM_ON fg_filestream
-- 1. 新增欄位 ALTER TABLE old_table ADD new_image VARBINARY(MAX) -- 2. 分批遷移 (避免長鎖定) UPDATE TOP (1000) old_table SET new_image = CAST(old_image AS VARBINARY(MAX)) WHERE new_image IS NULL -- 3. 驗證後切換應用、刪除舊欄位