C#でBitmap画像を保存する方法|Saveメソッドの使い方と形式指定をサンプルで解説
はじめに
C#でBitmap画像を保存するには、System.Drawing.BitmapクラスのSaveメソッドを使います。
Bitmapの保存処理は、画像編集ツール、帳票出力、スクリーンショット保存、サムネイル生成、画像変換、Webアプリでの画像アップロード処理など、実務でもよく使われます。
たとえば、次のような処理でBitmapの保存が必要になります。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("sample.png", ImageFormat.Png);
ただし、Bitmapの保存では、単にSaveメソッドを呼び出せばよいだけではありません。
保存形式の指定、JPEGの画質設定、保存先フォルダの存在確認、ファイルロック、Disposeによるリソース解放、.NET 6以降のSystem.Drawing.Commonの扱いなど、注意すべきポイントがあります。
この記事では、C#でBitmap画像を保存する方法について、Saveメソッドの基本から、PNG・JPEG・BMP・GIF・TIFF形式での保存、JPEG画質指定、形式変換、エラー対策、実務で使えるサンプルまで解説します。
1. C#でBitmap画像を保存する基本
C#でBitmapをファイルに保存する基本は、次の3ステップです。
Bitmapオブジェクトを作成または読み込むSaveメソッドで保存するusingまたはDisposeでリソースを解放する
最もシンプルな例は次のとおりです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("output.png", ImageFormat.Png);
BitmapはImageクラスを継承しており、画像保存にはImage.Saveメソッドを使用します。Saveメソッドには、ファイル名だけを指定するもの、画像形式を指定するもの、ストリームに保存するもの、エンコーダーを使って詳細な保存条件を指定するものがあります。Microsoft Learn
1-1. Bitmap保存で使うSaveメソッドとは
Saveメソッドは、Bitmap画像をファイルやストリームへ書き出すためのメソッドです。
主に使う overload は次の3つです。
C#bitmap.Save(string filename);
bitmap.Save(string filename, ImageFormat format);
bitmap.Save(Stream stream, ImageFormat format);
JPEGの画質を指定する場合は、次の overload を使います。
C#bitmap.Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams);
通常の保存であれば、まずは次の形式を使うのがおすすめです。
C#bitmap.Save("output.png", ImageFormat.Png);
ファイル名だけを指定するSave(string filename)も使えますが、拡張子と実際の保存形式の扱いで誤解が起きやすいため、実務ではImageFormatを明示したほうが安全です。
1-2. 最小コードでBitmapをファイルに保存する流れ
新規Bitmapを作成してPNG形式で保存する最小コードは次のとおりです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("sample.png", ImageFormat.Png);
このコードでは、幅300px、高さ200pxのBitmapを作成し、sample.pngという名前で保存しています。
ただし、このままだと中身は初期状態の画像です。背景色や文字、図形を描画してから保存したい場合は、Graphicsクラスを使います。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawString("Hello Bitmap", new Font("Arial", 20), Brushes.Black, new PointF(30, 80));
bitmap.Save("sample.png", ImageFormat.Png);
このように、Bitmapを作成し、Graphicsで描画し、最後にSaveで保存するのが基本的な流れです。
1-3. 保存先パスとファイル名を指定する基本ルール
Bitmapを保存するときは、保存先パスを文字列で指定します。
C#bitmap.Save(@"C:\Images\output.png", ImageFormat.Png);
C#では、Windowsのパス区切り文字\をそのまま書くとエスケープ文字として扱われるため、次のどちらかの書き方を使います。
C#bitmap.Save("C:\\Images\\output.png", ImageFormat.Png);
または、逐語的文字列リテラルを使います。
C#bitmap.Save(@"C:\Images\output.png", ImageFormat.Png);
実務では、Path.Combineを使ってパスを組み立てると安全です。
C#string folderPath = @"C:\Images";
string fileName = "output.png";
string filePath = Path.Combine(folderPath, fileName);
bitmap.Save(filePath, ImageFormat.Png);
Path.Combineを使うと、区切り文字の重複や不足を防ぎやすくなります。
2. Bitmap.Saveメソッドの使い方
Bitmap.Saveには複数の使い方があります。
よく使うのは、次の3パターンです。
C#Save(string filename)
Save(string filename, ImageFormat format)
Save(Stream stream, ImageFormat format)
それぞれの使い方を見ていきます。
2-1. Save(string filename)で保存する方法
Save(string filename)は、ファイル名だけを指定して保存する方法です。
C#using System.Drawing;
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("output.bmp");
この書き方は簡単ですが、保存形式を明示していない点に注意が必要です。
特に、次のようなコードは避けたほうが安全です。
C#bitmap.Save("output.jpg");
ファイル名の拡張子を.jpgにしたからといって、常に意図したJPEG形式で保存されると考えるのは危険です。既存画像の形式やBitmapの状態によって、期待と異なる結果になることがあります。
実務では、次のように保存形式を明示する書き方をおすすめします。
C#bitmap.Save("output.jpg", ImageFormat.Jpeg);
2-2. Save(string filename, ImageFormat format)で形式を指定する方法
最もよく使うのが、ファイル名と画像形式を指定する方法です。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("output.png", ImageFormat.Png);
ImageFormatには、PNG、JPEG、BMP、GIF、TIFFなどの形式を指定できます。ImageFormatクラスは、保存時に画像形式を指定するために使われるクラスです。Microsoft Learn
主な指定例は次のとおりです。
C#bitmap.Save("image.png", ImageFormat.Png);
bitmap.Save("image.jpg", ImageFormat.Jpeg);
bitmap.Save("image.bmp", ImageFormat.Bmp);
bitmap.Save("image.gif", ImageFormat.Gif);
bitmap.Save("image.tiff", ImageFormat.Tiff);
拡張子とImageFormatは一致させるのが基本です。
たとえば、次のようなコードは避けましょう。
C#bitmap.Save("image.jpg", ImageFormat.Png);
この場合、ファイル名はJPEGのように見えますが、中身はPNG形式になります。後から画像を開くアプリケーションによっては、正しく認識されない原因になります。
2-3. Save(Stream stream, ImageFormat format)でストリームに保存する方法
ファイルではなく、MemoryStreamやFileStreamなどのストリームに保存することもできます。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using MemoryStream stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
byte[] bytes = stream.ToArray();
この方法は、画像をファイルに保存せず、バイト配列として扱いたい場合に便利です。
たとえば、次のようなケースで使います。
Web APIで画像をレスポンスとして返す
データベースに画像を保存する
画像をBase64文字列に変換する
一時ファイルを作らずに画像を加工する
Save(Stream, ImageFormat)は、指定したストリームに指定形式で画像を書き込むためのメソッドです。Microsoft Learn
3. 画像形式を指定して保存する方法
Bitmapは、さまざまな画像形式で保存できます。
ここでは、よく使われる次の形式についてサンプルを紹介します。
PNG
JPEG
BMP
GIF
TIFF
3-1. PNG形式で保存するサンプル
PNG形式で保存するには、ImageFormat.Pngを指定します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawEllipse(Pens.Blue, 50, 40, 200, 100);
bitmap.Save("output.png", ImageFormat.Png);
PNGは、画質劣化のない可逆圧縮形式です。
また、透明背景を保持できるため、ロゴ、アイコン、UI素材、透過画像の保存に向いています。
透明背景のBitmapをPNGで保存する例は次のとおりです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.Transparent);
graphics.FillEllipse(Brushes.Red, 50, 40, 200, 100);
bitmap.Save("transparent.png", ImageFormat.Png);
透明度を維持したい場合は、JPEGではなくPNGを選びましょう。
3-2. JPEG形式で保存するサンプル
JPEG形式で保存するには、ImageFormat.Jpegを指定します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawString("JPEG Sample", new Font("Arial", 24), Brushes.Black, new PointF(40, 80));
bitmap.Save("output.jpg", ImageFormat.Jpeg);
JPEGは写真の保存に向いている形式です。
一方で、JPEGは非可逆圧縮のため、保存するたびに画質が劣化する可能性があります。また、透明度を扱えません。
透明部分を含むBitmapをJPEGで保存すると、背景が黒くなる、または意図しない色になる場合があります。JPEGで保存する前に、白などの背景色を描画しておくと安全です。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap source = new Bitmap("transparent.png");
using Bitmap jpegBitmap = new Bitmap(source.Width, source.Height);
using Graphics graphics = Graphics.FromImage(jpegBitmap);
graphics.Clear(Color.White);
graphics.DrawImage(source, 0, 0);
jpegBitmap.Save("output.jpg", ImageFormat.Jpeg);
3-3. BMP形式で保存するサンプル
BMP形式で保存するには、ImageFormat.Bmpを指定します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.LightGray);
graphics.FillRectangle(Brushes.Green, 50, 50, 200, 100);
bitmap.Save("output.bmp", ImageFormat.Bmp);
BMPはWindowsで古くから使われている画像形式です。
圧縮されない、または圧縮率が低いことが多いため、ファイルサイズは大きくなりやすいですが、扱いが単純で検証用途や一時出力に使いやすい形式です。
3-4. GIF形式で保存するサンプル
GIF形式で保存するには、ImageFormat.Gifを指定します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.FillEllipse(Brushes.Orange, 80, 40, 140, 100);
bitmap.Save("output.gif", ImageFormat.Gif);
GIFは、色数が少ない画像や簡単な図形に向いています。
ただし、GIFは256色までのパレット形式が基本で、写真のような色数の多い画像には向きません。また、System.Drawingの通常のSaveだけでアニメーションGIFを簡単に作成できるわけではありません。
静止画のGIFとして保存したい場合に使うとよいでしょう。
3-5. TIFF形式で保存するサンプル
TIFF形式で保存するには、ImageFormat.Tiffを指定します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawRectangle(Pens.Black, 50, 50, 200, 100);
bitmap.Save("output.tiff", ImageFormat.Tiff);
TIFFは、スキャン画像、印刷用画像、業務システムの画像管理などで使われることがあります。
ただし、TIFFは圧縮方式や複数ページ対応など仕様の幅が広いため、単純な保存で十分かどうかは利用先のシステムに合わせて確認しましょう。
4. JPEGの画質を指定して保存する方法
ImageFormat.Jpegを指定するだけでもJPEG保存はできますが、画質を細かく指定したい場合は、ImageCodecInfoとEncoderParametersを使います。
JPEG保存時に圧縮レベルを指定するには、JPEGエンコーダーを取得し、EncoderParametersに品質パラメーターを設定して、Saveメソッドへ渡します。Microsoft Learn
4-1. ImageCodecInfoを使ってJPEGエンコーダーを取得する
まず、JPEG用のエンコーダーを取得するメソッドを用意します。
C#using System.Drawing.Imaging;
using System.Linq;
static ImageCodecInfo GetEncoder(ImageFormat format)
{
return ImageCodecInfo.GetImageEncoders()
.First(codec => codec.FormatID == format.Guid);
}
JPEGエンコーダーを取得する場合は、次のように呼び出します。
C#ImageCodecInfo jpegEncoder = GetEncoder(ImageFormat.Jpeg);
ImageCodecInfo.GetImageEncoders()は、利用可能な画像エンコーダーの一覧を取得します。その中からImageFormat.Jpegに対応するエンコーダーを探します。
4-2. EncoderParametersで画質を指定する
JPEGの画質を指定するには、Encoder.Qualityを使います。
C#using System.Drawing.Imaging;
EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
画質の値は一般的に0Lから100Lの範囲で指定します。
100Lに近いほど高画質・大きいファイルサイズ0Lに近いほど低画質・小さいファイルサイズ
実務では、画質とファイルサイズのバランスを考えると、80L〜90Lあたりが使いやすいことが多いです。
4-3. 圧縮率を変えてJPEG保存するサンプル
JPEGの画質を指定して保存する完全なサンプルは次のとおりです。
C#using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using Bitmap bitmap = new Bitmap("input.png");
SaveJpeg(bitmap, "quality_100.jpg", 100L);
SaveJpeg(bitmap, "quality_80.jpg", 80L);
SaveJpeg(bitmap, "quality_50.jpg", 50L);
static void SaveJpeg(Bitmap bitmap, string filePath, long quality)
{
ImageCodecInfo jpegEncoder = ImageCodecInfo.GetImageEncoders()
.First(codec => codec.FormatID == ImageFormat.Jpeg.Guid);
using EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
bitmap.Save(filePath, jpegEncoder, encoderParams);
}
このコードでは、同じBitmapを画質100、80、50で保存しています。
画質を下げるほどファイルサイズは小さくなりやすいですが、ブロックノイズやにじみが目立つ場合があります。写真では多少圧縮しても目立ちにくい一方、文字や線画では劣化が目立ちやすいため注意しましょう。
5. 既存画像を読み込んで別形式で保存する方法
既存の画像ファイルをBitmapとして読み込み、別形式で保存することもできます。
たとえば、PNGをJPEGに変換したり、BMPをPNGに変換したりできます。
5-1. Bitmapで画像ファイルを読み込む
既存画像を読み込むには、Bitmapのコンストラクターにファイルパスを指定します。
C#using System.Drawing;
using Bitmap bitmap = new Bitmap("input.png");
読み込んだBitmapは、Saveメソッドで別の形式に保存できます。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap("input.png");
bitmap.Save("output.jpg", ImageFormat.Jpeg);
ただし、読み込んだ画像ファイルはBitmapが破棄されるまでロックされることがあります。上書き保存や削除を行う場合は、ファイルロックに注意が必要です。
5-2. PNGからJPEGへ変換して保存する
PNGをJPEGに変換するサンプルです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap source = new Bitmap("input.png");
using Bitmap jpegBitmap = new Bitmap(source.Width, source.Height);
using Graphics graphics = Graphics.FromImage(jpegBitmap);
graphics.Clear(Color.White);
graphics.DrawImage(source, 0, 0, source.Width, source.Height);
jpegBitmap.Save("output.jpg", ImageFormat.Jpeg);
PNGには透明度が含まれる場合がありますが、JPEGは透明度を保持できません。
そのため、JPEGへ変換する前に白背景を描画しています。背景色を黒、グレー、任意の色にしたい場合は、graphics.Clear(Color.White)の部分を変更します。
5-3. BMPからPNGへ変換して保存する
BMPをPNGに変換するサンプルです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap("input.bmp");
bitmap.Save("output.png", ImageFormat.Png);
BMPはファイルサイズが大きくなりやすいため、PNGへ変換するとファイルサイズを抑えられる場合があります。
ただし、画像の内容によって圧縮効果は変わります。写真のような画像ではJPEGのほうが小さくなる場合もあります。
5-4. 形式変換時の注意点
画像形式を変換するときは、次の点に注意してください。
まず、透明度を保持したい場合はPNGを使います。
C#bitmap.Save("output.png", ImageFormat.Png);
JPEGは透明度を保持できないため、透過画像をJPEGに変換すると背景色が失われます。
次に、JPEGは保存時に画質が劣化する可能性があります。
C#bitmap.Save("output.jpg", ImageFormat.Jpeg);
画像を何度もJPEGで読み込み・保存すると、劣化が蓄積することがあります。編集途中のデータはPNGやBMPなどの形式で保持し、最終出力だけJPEGにする方法もあります。
また、拡張子とImageFormatは一致させましょう。
C#bitmap.Save("output.png", ImageFormat.Png); // OK
bitmap.Save("output.jpg", ImageFormat.Jpeg); // OK
次のような指定は避けます。
C#bitmap.Save("output.jpg", ImageFormat.Png); // 非推奨
ファイル名だけ見るとJPEGに見えますが、中身はPNGです。画像ビューアーやアップロード先でエラーになる原因になります。
6. 新規Bitmapを作成して保存する方法
既存画像を読み込むだけでなく、新しくBitmapを作成して保存することもできます。
画像生成、サムネイル作成、バーコード作成、グラフ画像出力、帳票画像出力などで使います。
6-1. 空のBitmapを作成する
空のBitmapを作成するには、幅と高さを指定します。
C#using System.Drawing;
using Bitmap bitmap = new Bitmap(400, 300);
この状態では、Bitmapの中身は明示的に初期化されていないため、通常はGraphics.Clearで背景を塗りつぶします。
C#using System.Drawing;
using Bitmap bitmap = new Bitmap(400, 300);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
透明背景にしたい場合は、次のようにします。
C#graphics.Clear(Color.Transparent);
ただし、透明背景を維持して保存するにはPNGなど透明度に対応した形式を選ぶ必要があります。
6-2. Graphicsで描画してから保存する
Bitmapに文字や図形を描画するには、Graphics.FromImageを使います。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(500, 300);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
using Font font = new Font("Arial", 28);
graphics.DrawString("Bitmap Save", font, Brushes.Black, new PointF(100, 120));
graphics.DrawRectangle(Pens.Blue, 50, 50, 400, 200);
bitmap.Save("drawn.png", ImageFormat.Png);
Graphics、Font、Bitmapはいずれもリソースを使用するため、usingで確実に破棄するのが基本です。
6-3. 指定サイズの画像を生成して保存するサンプル
指定サイズの画像を生成して保存する実用的なサンプルです。
C#using System.Drawing;
using System.Drawing.Imaging;
static void CreateImage(string filePath, int width, int height, string text)
{
using Bitmap bitmap = new Bitmap(width, height);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
using Font font = new Font("Arial", 24);
using Brush brush = new SolidBrush(Color.Black);
SizeF textSize = graphics.MeasureString(text, font);
float x = (width - textSize.Width) / 2;
float y = (height - textSize.Height) / 2;
graphics.DrawString(text, font, brush, x, y);
bitmap.Save(filePath, ImageFormat.Png);
}
CreateImage("generated.png", 600, 300, "Hello C# Bitmap");
このコードでは、指定した幅と高さの画像を作成し、中央に文字を描画してPNGで保存しています。
7. Bitmap保存時によくあるエラーと対処法
Bitmap保存では、次のようなエラーがよく発生します。
GDI+で汎用エラーが発生する
保存先フォルダが存在しない
ファイルが使用中で上書きできない
権限不足で保存できない
拡張子と保存形式が一致していない
特に多いのが、ExternalExceptionとして表示されるGDI+の汎用エラーです。
7-1. GDI+で汎用エラーが発生する原因
Bitmap保存時に次のようなエラーが出ることがあります。
A generic error occurred in GDI+.
日本語環境では、次のように表示されることもあります。
GDI+ で汎用エラーが発生しました。
このエラーは原因が広く、メッセージだけでは詳細がわかりにくいのが特徴です。
主な原因は次のとおりです。
保存先フォルダが存在しない
保存先に書き込み権限がない
同名ファイルが別プロセスで使用中
読み込んだ画像と同じファイルへ上書き保存しようとしている
ストリームが閉じられている
不正なパスを指定している
使用できない画像形式やエンコーダーを指定している
対処するには、まず保存先フォルダ、権限、ファイルロックを確認しましょう。
7-2. 保存先フォルダが存在しない場合の対処法
存在しないフォルダに保存しようとすると、保存に失敗します。
C#bitmap.Save(@"C:\NotExists\output.png", ImageFormat.Png);
保存前にフォルダを作成しておくと安全です。
C#using System.Drawing;
using System.Drawing.Imaging;
string folderPath = @"C:\Images";
string filePath = Path.Combine(folderPath, "output.png");
Directory.CreateDirectory(folderPath);
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save(filePath, ImageFormat.Png);
Directory.CreateDirectoryは、フォルダが既に存在していても例外にならないため、保存前に呼び出しておくと便利です。
7-3. ファイルが使用中で上書きできない場合の対処法
既存画像を読み込み、その同じファイルに上書き保存しようとすると、ファイルロックが原因で失敗することがあります。
避けたほうがよい例です。
C#using Bitmap bitmap = new Bitmap("image.png");
bitmap.Save("image.png", ImageFormat.Png);
この場合、image.pngを読み込み中のBitmapがファイルを保持しているため、同じファイルへ保存できないことがあります。
対処法として、いったん別のBitmapにコピーしてから元ファイルを解放します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap loaded = new Bitmap("image.png");
using Bitmap copy = new Bitmap(loaded);
copy.Save("image_copy.png", ImageFormat.Png);
同じファイルへ上書きしたい場合は、一時ファイルに保存してから置き換える方法が安全です。
C#string filePath = "image.png";
string tempPath = "image_temp.png";
using (Bitmap loaded = new Bitmap(filePath))
using (Bitmap copy = new Bitmap(loaded))
{
copy.Save(tempPath, ImageFormat.Png);
}
File.Delete(filePath);
File.Move(tempPath, filePath);
7-4. 権限不足で保存できない場合の対処法
次のような場所には、アプリケーションの実行権限によって保存できない場合があります。
C:\WindowsC:\Program Files他ユーザーのフォルダ
管理者権限が必要なフォルダ
保存できない場合は、ユーザーのドキュメントフォルダやアプリケーションデータフォルダを使うと安全です。
C#string folderPath = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
"MyImages"
);
Directory.CreateDirectory(folderPath);
string filePath = Path.Combine(folderPath, "output.png");
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save(filePath, ImageFormat.Png);
Webアプリケーションの場合は、アプリケーションが書き込み可能なディレクトリを選び、実行ユーザーに権限があるか確認しましょう。
7-5. 拡張子とImageFormatが一致しない場合の注意点
次のコードは、ファイル名は.jpgですが、実際にはPNG形式で保存しています。
C#bitmap.Save("output.jpg", ImageFormat.Png);
このようなファイルは、アプリケーションによっては開ける場合もありますが、アップロード処理や画像検証処理でエラーになることがあります。
正しくは、拡張子とImageFormatを一致させます。
C#bitmap.Save("output.png", ImageFormat.Png);
bitmap.Save("output.jpg", ImageFormat.Jpeg);
bitmap.Save("output.bmp", ImageFormat.Bmp);
画像保存では、ファイル名の見た目と中身の形式を一致させることが重要です。
8. Bitmap保存で重要なリソース解放
Bitmap保存では、リソース解放が非常に重要です。
Bitmap、Graphics、Stream、Font、Brushなどは、使い終わったら破棄する必要があります。
リソース解放を怠ると、次のような問題が起きます。
ファイルがロックされたままになる
上書き保存できない
メモリ使用量が増える
GDI+エラーが発生する
大量処理でアプリケーションが不安定になる
8-1. usingでBitmapを確実にDisposeする
Bitmapはusingで囲むのが基本です。
C#using Bitmap bitmap = new Bitmap("input.png");
bitmap.Save("output.png", ImageFormat.Png);
古い書き方では、次のようにusingブロックを使います。
C#using (Bitmap bitmap = new Bitmap("input.png"))
{
bitmap.Save("output.png", ImageFormat.Png);
}
usingを使うと、処理が正常終了した場合だけでなく、例外が発生した場合でもDisposeが呼び出されます。
8-2. GraphicsやStreamもDisposeする
GraphicsやMemoryStreamも同様にDisposeが必要です。
C#using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
bitmap.Save("output.png", ImageFormat.Png);
MemoryStreamに保存する場合もusingを使います。
C#using Bitmap bitmap = new Bitmap(300, 200);
using MemoryStream stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
byte[] bytes = stream.ToArray();
FileStreamを使う場合も同じです。
C#using Bitmap bitmap = new Bitmap(300, 200);
using FileStream stream = new FileStream("output.png", FileMode.Create, FileAccess.Write);
bitmap.Save(stream, ImageFormat.Png);
8-3. ファイルロックを防ぐ書き方
画像ファイルを読み込んだ後、元ファイルをすぐに解放したい場合は、いったんメモリ上にコピーする方法があります。
C#using System.Drawing;
static Bitmap LoadBitmapWithoutLock(string filePath)
{
using Bitmap temp = new Bitmap(filePath);
return new Bitmap(temp);
}
使い方は次のとおりです。
C#using Bitmap bitmap = LoadBitmapWithoutLock("input.png");
File.Delete("input.png");
bitmap.Save("output.png", ImageFormat.Png);
この方法では、tempが破棄された時点で元ファイルのロックを解放し、コピーしたBitmapだけを使って処理できます。
ファイルロックを避けたい実務コードでは、画像を直接保持し続けるのではなく、必要に応じてコピーを作ると安定します。
9. 実務で使いやすいBitmap保存のサンプル集
ここでは、実務でそのまま使いやすいBitmap保存のサンプルを紹介します。
日時付きファイル名、フォルダ自動作成、上書き保存、別名保存、バイト配列変換など、よく使う処理をまとめます。
9-1. 日時付きファイル名で保存する
日時付きファイル名で保存すると、ファイル名の重複を防ぎやすくなります。
C#using System.Drawing;
using System.Drawing.Imaging;
string folderPath = @"C:\Images";
Directory.CreateDirectory(folderPath);
string fileName = $"image_{DateTime.Now:yyyyMMdd_HHmmss}.png";
string filePath = Path.Combine(folderPath, fileName);
using Bitmap bitmap = new Bitmap(300, 200);
using Graphics graphics = Graphics.FromImage(bitmap);
graphics.Clear(Color.White);
graphics.DrawString("DateTime File", new Font("Arial", 20), Brushes.Black, new PointF(40, 80));
bitmap.Save(filePath, ImageFormat.Png);
ミリ秒まで含めたい場合は、次のようにします。
C#string fileName = $"image_{DateTime.Now:yyyyMMdd_HHmmss_fff}.png";
短時間に連続保存する場合は、ミリ秒やGUIDを含めると重複を避けやすくなります。
9-2. 保存前にフォルダを自動作成する
保存先フォルダを自動作成するサンプルです。
C#using System.Drawing;
using System.Drawing.Imaging;
static void SaveBitmapWithDirectory(Bitmap bitmap, string filePath, ImageFormat format)
{
string? directory = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directory))
{
Directory.CreateDirectory(directory);
}
bitmap.Save(filePath, format);
}
使い方は次のとおりです。
C#using Bitmap bitmap = new Bitmap(300, 200);
SaveBitmapWithDirectory(bitmap, @"C:\Images\Sample\output.png", ImageFormat.Png);
保存処理の前に毎回Directory.CreateDirectoryを呼び出しておくと、フォルダが存在しないことによるエラーを防げます。
9-3. 同名ファイルを上書き保存する
同名ファイルを上書き保存する場合、通常はSaveだけでも既存ファイルに書き込みます。
C#using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("output.png", ImageFormat.Png);
ただし、既存ファイルが使用中の場合は失敗します。
より明示的に上書きしたい場合は、FileStreamを使ってFileMode.Createを指定します。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
using FileStream stream = new FileStream("output.png", FileMode.Create, FileAccess.Write);
bitmap.Save(stream, ImageFormat.Png);
FileMode.Createは、ファイルが存在する場合は上書きし、存在しない場合は新規作成します。
9-4. 上書きせず別名で保存する
既存ファイルを上書きせず、別名で保存するサンプルです。
C#static string GetUniqueFilePath(string filePath)
{
if (!File.Exists(filePath))
{
return filePath;
}
string? directory = Path.GetDirectoryName(filePath);
string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(filePath);
string extension = Path.GetExtension(filePath);
int count = 1;
while (true)
{
string newFileName = $"{fileNameWithoutExtension}_{count}{extension}";
string newFilePath = Path.Combine(directory ?? "", newFileName);
if (!File.Exists(newFilePath))
{
return newFilePath;
}
count++;
}
}
使い方は次のとおりです。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
string filePath = GetUniqueFilePath(@"C:\Images\output.png");
bitmap.Save(filePath, ImageFormat.Png);
output.pngが既に存在する場合、output_1.png、output_2.pngのように別名で保存されます。
9-5. MemoryStreamに保存してバイト配列へ変換する
Bitmapをファイルではなく、バイト配列に変換するサンプルです。
C#using System.Drawing;
using System.Drawing.Imaging;
static byte[] BitmapToBytes(Bitmap bitmap, ImageFormat format)
{
using MemoryStream stream = new MemoryStream();
bitmap.Save(stream, format);
return stream.ToArray();
}
使い方は次のとおりです。
C#using Bitmap bitmap = new Bitmap(300, 200);
byte[] pngBytes = BitmapToBytes(bitmap, ImageFormat.Png);
Web APIで画像を返す場合や、データベースに画像を保存する場合に便利です。
Base64文字列に変換する場合は、次のようにします。
C#string base64 = Convert.ToBase64String(pngBytes);
10. .NET環境別の注意点
Bitmap保存は、使用する.NET環境によって注意点が異なります。
特に、.NET Frameworkと.NET 6以降では、System.Drawing.Commonの扱いに違いがあります。
10-1. .NET FrameworkでBitmapを保存する場合
.NET FrameworkのWindowsアプリケーションでは、System.Drawingは一般的によく使われます。
Windows Formsアプリケーションでは、次の名前空間を使います。
C#using System.Drawing;
using System.Drawing.Imaging;
.NET Frameworkでは、Windows環境でGDI+を利用してBitmapを扱うことが多く、古い業務アプリケーションでもよく使われています。
ただし、.NET Frameworkでも、Bitmap、Graphics、StreamのDisposeは必須です。大量の画像を処理する場合は、リソースリークやファイルロックに注意しましょう。
10-2. .NET 6以降でSystem.Drawing.Commonを使う場合
.NET 6以降では、System.Drawing.Commonの扱いに注意が必要です。
Microsoftのドキュメントでは、.NET 6以降のSystem.Drawing.CommonはWindows専用として扱われ、非Windows環境ではコンパイル時警告や実行時例外の対象になると説明されています。Microsoft Learn+1
Windows環境で使う場合は、プロジェクトにSystem.Drawing.Commonを追加して利用することがあります。
XML<ItemGroup>
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
</ItemGroup>
Windows FormsやWPFなど、WindowsデスクトップアプリでBitmap保存を行う場合は、従来どおりSystem.Drawingを使える場面が多いです。
一方で、ASP.NET Coreやコンソールアプリでクロスプラットフォーム実行を想定している場合は、System.Drawing.Commonに依存しすぎない設計を検討したほうが安全です。
10-3. Windows以外の環境でBitmap保存する際の注意点
LinuxやmacOS、Dockerコンテナなど、Windows以外の環境で画像保存を行う場合、System.Drawing.Commonは避けるのが基本です。
.NET 7以降では、.NET 6で一時的に使えた非Windows向けの構成スイッチも削除され、非Windows OSでSystem.Drawing.Commonを使用できなくなったと説明されています。Microsoft Learn
クロスプラットフォームで画像処理を行う場合は、次のようなライブラリを検討します。
SkiaSharp
ImageSharp
Magick.NET
Microsoft.Maui.Graphics
たとえば、サーバーサイドで画像リサイズやサムネイル生成を行う場合は、最初からクロスプラットフォーム対応の画像処理ライブラリを選んだほうが運用しやすいです。
Windowsアプリだけで完結するならSystem.Drawing、LinuxやDockerでも動かすなら代替ライブラリ、という判断が実務では重要です。
11. Bitmap保存に関するよくある質問
ここでは、C#のBitmap保存でよくある質問に答えます。
11-1. 拡張子だけで保存形式は変わるのか
基本的には、拡張子だけに頼るべきではありません。
次のように、保存形式を明示するのがおすすめです。
C#bitmap.Save("output.png", ImageFormat.Png);
bitmap.Save("output.jpg", ImageFormat.Jpeg);
ファイル名の拡張子は、人間やアプリケーションが形式を判断するための目印です。一方、実際の画像形式は保存時のエンコーダーによって決まります。
そのため、次のようなコードは避けましょう。
C#bitmap.Save("output.jpg", ImageFormat.Png);
この場合、拡張子は.jpgですが、画像の中身はPNG形式になります。
11-2. JPEG保存時に背景が黒くなる原因は何か
JPEGは透明度をサポートしていません。
そのため、透明背景を持つPNGなどをJPEGに変換すると、透明部分が黒くなる、または意図しない色になる場合があります。
対策として、JPEG保存前に背景色を塗りつぶします。
C#using Bitmap source = new Bitmap("transparent.png");
using Bitmap output = new Bitmap(source.Width, source.Height);
using Graphics graphics = Graphics.FromImage(output);
graphics.Clear(Color.White);
graphics.DrawImage(source, 0, 0);
output.Save("output.jpg", ImageFormat.Jpeg);
白背景にしたい場合はColor.White、黒背景にしたい場合はColor.Blackを指定します。
11-3. 透明背景を維持して保存するにはどの形式を選ぶべきか
透明背景を維持したい場合は、PNG形式を選びます。
C#bitmap.Save("output.png", ImageFormat.Png);
PNGは透明度を扱えるため、ロゴ、アイコン、切り抜き画像、UI素材に向いています。
JPEGは透明度を保持できないため、透過画像の保存には向きません。
GIFも透明色を扱えますが、色数制限があり、半透明の表現には向きません。通常の透過画像保存ではPNGを選ぶのが無難です。
11-4. WebP形式で保存できるのか
System.Drawing.Imaging.ImageFormatには、標準のWebP指定は用意されていません。
そのため、bitmap.Save("output.webp", ImageFormat.WebP)のような書き方はできません。
WebPで保存したい場合は、次のような方法を検討します。
ImageSharpを使う
SkiaSharpを使う
Magick.NETを使う
WebP対応の別ライブラリを使う
WebアプリケーションでWebP変換が必要な場合は、System.Drawingではなく、最初からWebP対応ライブラリを選ぶのがおすすめです。
11-5. 保存した画像が破損する原因は何か
保存した画像が破損する原因としては、次のようなものがあります。
拡張子と実際の保存形式が一致していない
保存中にストリームを途中で閉じている
MemoryStreamの位置を戻さずに読み込んでいるファイル書き込みが完了する前に読み取っている
例外発生時に不完全なファイルが残っている
複数スレッドや複数プロセスから同じファイルへ同時に書き込んでいる
MemoryStreamに保存した後で読み込む場合は、必要に応じて位置を先頭に戻します。
C#using MemoryStream stream = new MemoryStream();
bitmap.Save(stream, ImageFormat.Png);
stream.Position = 0;
// streamを読み込み処理に渡す
バイト配列として取り出すだけなら、ToArrayを使えば問題ありません。
C#byte[] bytes = stream.ToArray();
ファイルに保存する場合は、保存処理が完了してから読み込み、同じファイルに同時アクセスしないようにしましょう。
まとめ
C#でBitmap画像を保存するには、Bitmap.Saveメソッドを使います。
基本的な保存は、次のように書けます。
C#using System.Drawing;
using System.Drawing.Imaging;
using Bitmap bitmap = new Bitmap(300, 200);
bitmap.Save("output.png", ImageFormat.Png);
実務では、ファイル名だけを指定するよりも、ImageFormatで保存形式を明示するのがおすすめです。
C#bitmap.Save("output.png", ImageFormat.Png);
bitmap.Save("output.jpg", ImageFormat.Jpeg);
bitmap.Save("output.bmp", ImageFormat.Bmp);
bitmap.Save("output.gif", ImageFormat.Gif);
bitmap.Save("output.tiff", ImageFormat.Tiff);
JPEGの画質を指定したい場合は、ImageCodecInfoとEncoderParametersを使います。
C#using EncoderParameters encoderParams = new EncoderParameters(1);
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, 90L);
bitmap.Save("output.jpg", jpegEncoder, encoderParams);
また、Bitmap保存では、保存形式だけでなく、次の点も重要です。
保存先フォルダを事前に作成する
拡張子と
ImageFormatを一致させるJPEG保存時は透明背景に注意する
Bitmap、Graphics、Streamをusingで破棄するファイルロックを避ける
.NET 6以降の
System.Drawing.CommonはWindows中心で使うLinuxやDockerなど非Windows環境では代替ライブラリを検討する
C#でBitmapを安全に保存するには、Saveメソッドの使い方だけでなく、画像形式、リソース解放、実行環境まで含めて設計することが大切です。

