在資料庫儲存圖片(`varbinary`/`BLOB`)、網路傳輸、快取等場景,常需在 Image、byte[]、Stream、檔案之間轉換。本文提供三個核心方法:檔案→位元組陣列、位元組陣列→Image、位元組陣列→檔案,並說明記憶體流管理與常見坑點。
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
public class ImageConverter
{
private byte[] _buffer; // 內部快取
public string SourcePath { get; set; }
public string TargetPath { get; set; }
// 1. 檔案 → byte[] (儲存至 _buffer)
public void FileToBuffer()
{
using (Image img = Image.FromFile(SourcePath))
using (MemoryStream ms = new MemoryStream())
{
img.Save(ms, ImageFormat.Bmp); // 或 Png/Jpeg 依需求
_buffer = ms.ToArray();
}
}
// 2. byte[] → Image (供 PictureBox 顯示)
public Image BufferToImage()
{
if (_buffer == null || _buffer.Length == 0) return null;
using (MemoryStream ms = new MemoryStream(_buffer))
{
// 重要:FromStream 要求 stream 保持開啟直到 Image 不再使用
// 若需立即關閉 stream,請先複製像素資料:
Image img = Image.FromStream(ms);
// 強制載入像素資料,脫離 stream 依賴
var bmp = new Bitmap(img);
return bmp;
}
}
// 3. byte[] → 檔案
public void BufferToFile()
{
if (_buffer == null) return;
using (FileStream fs = new FileStream(TargetPath, FileMode.Create, FileAccess.Write))
{
fs.Write(_buffer, 0, _buffer.Length);
fs.Flush();
}
}
}
| 問題 | 原因 | 解決 |
|---|---|---|
Image.FromStream 關閉 stream 後報錯 | GDI+ 延遲載入,stream 必須保持開啟 | 在 `using` 區塊內 `new Bitmap(img)` 強制複製像素,或不關閉 stream(由外部管理生命週期) |
| 大圖記憶體暴增 | 一次 `ToArray()` 載入完整像素 | 分塊處理、使用 `Image.FromStream` 串流式處理、或改用 `Bitmap.LockBits` |
| 儲存格式與原檔不符 | 範例固定用 `ImageFormat.Bmp` | 根據副檔名或 `Image.RawFormat` 動態決定格式 |
| `Image.FromFile` 鎖定檔案 | 檔案流未釋放 | 改用 `FileStream` + `Image.FromStream`,或複製檔案到暫存目錄 |
// 存入 SQL Server varbinary(max)
public void SaveToDb(int id, byte[] imgData)
{
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand("UPDATE Users SET Photo=@p WHERE Id=@id", conn))
{
cmd.Parameters.Add("@p", SqlDbType.VarBinary, -1).Value = imgData;
cmd.Parameters.AddWithValue("@id", id);
conn.Open(); cmd.ExecuteNonQuery();
}
}
// 讀出並顯示
public Image LoadFromDb(int id)
{
using (var conn = new SqlConnection(connStr))
using (var cmd = new SqlCommand("SELECT Photo FROM Users WHERE Id=@id", conn))
{
cmd.Parameters.AddWithValue("@id", id);
conn.Open();
byte[] data = (byte[])cmd.ExecuteScalar();
if (data == null) return null;
using (var ms = new MemoryStream(data))
{
Image img = Image.FromStream(ms);
return new Bitmap(img); // 脫離 stream
}
}
}
File → byte[]:`Image.FromFile` → `MemoryStream` → `ToArray()`。byte[] → Image:`MemoryStream` → `Image.FromStream` → `new Bitmap()` 脫離流。byte[] → File:`FileStream.Write`。