常見需求:圖片檔案 ↔ `byte[]` ↔ `MemoryStream` ↔ `Image`/`Bitmap` 互轉。本文提供標準、可重用的擴充方法。
public static class ImageExtensions
{
// 檔案 → byte[]
public static byte[] FileToBytes(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
var bytes = new byte[fs.Length];
fs.Read(bytes, 0, bytes.Length);
return bytes;
}
}
// byte[] → Image
public static Image BytesToImage(byte[] bytes)
{
using (var ms = new MemoryStream(bytes))
{
return Image.FromStream(ms); // 必須保持 ms 開啟直到 Image 不再使用
}
}
// Image → byte[] (指定格式)
public static byte[] ImageToBytes(this Image img, ImageFormat format = null)
{
using (var ms = new MemoryStream())
{
img.Save(ms, format ?? ImageFormat.Png);
return ms.ToArray();
}
}
// byte[] → 檔案
public static void BytesToFile(byte[] bytes, string path)
{
File.WriteAllBytes(path, bytes);
}
// 檔案 → Image (一步到位)
public static Image FileToImage(string path) => Image.FromFile(path);
// Image → 檔案
public static void ImageToFile(this Image img, string path, ImageFormat format = null)
=> img.Save(path, format ?? ImageFormat.Png);
}
// 1. 檔案 → byte[] → 資料庫儲存
byte[] photoBytes = ImageExtensions.FileToBytes(@"C:\photo.jpg");
SaveToDb(photoBytes);
// 2. 讀取 byte[] → 顯示於 PictureBox
byte[] dbBytes = LoadFromDb();
pictureBox.Image = ImageExtensions.BytesToImage(dbBytes);
// 3. PictureBox 影像 → byte[] → 另存新檔
byte[] outBytes = pictureBox.Image.ImageToBytes(ImageFormat.Jpeg);
ImageExtensions.BytesToFile(outBytes, @"C:\output.jpg");
| 議題 | 說明 |
|---|---|
| MemoryStream 生命週期 | `Image.FromStream` 要求串流在 Image 存活期間保持開啟。若需關閉串流,請先 `img = new Bitmap(Image.FromStream(ms))` 建立獨立副本。 |
| 格式選擇 | PNG 無損、支援透明;JPEG 有損、體積小、適合照片;BMP 未壓縮、體積大;GIF 動畫/256 色。 |
| 大檔案記憶體 | `byte[]` 會完整載入記憶體。超大圖片 (>50MB) 建議改用 `FileStream` 直接讀寫或分塊處理。 |
| EXIF 方向 | 手機相片常含 EXIF 旋轉標籤,`Image.FromStream` 不自動旋轉。顯示前需檢查 `PropertyItems` 並手動旋轉/翻轉。 |
| 解碼器安全性 | 惡意圖片可能觸發解碼器漏洞。生產環境考慮使用 `System.Drawing.Common` 替代品 (ImageSharp, SkiaSharp) 或沙箱隔離。 |
`System.Drawing.Common` 僅支援 Windows。Linux/macOS/容器請改用:
以上庫皆提供 `byte[]` ↔ `Image` ↔ `Stream` 完整轉換 API,語法極為相似。