メモリストリームが閉じているため、Image.Save(..)はGDI +例外をスローします


108

画像として保存したいバイナリデータがいくつかあります。画像を保存しようとすると、画像の作成に使用されたメモリストリームが保存前に閉じられていた場合、例外がスローされます。これを行う理由は、動的にイメージを作成しているためです。メモリストリームを使用する必要があります。

これはコードです:

[TestMethod]
public void TestMethod1()
{
    // Grab the binary data.
    byte[] data = File.ReadAllBytes("Chick.jpg");

    // Read in the data but do not close, before using the stream.
    Stream originalBinaryDataStream = new MemoryStream(data);
    Bitmap image = new Bitmap(originalBinaryDataStream);
    image.Save(@"c:\test.jpg");
    originalBinaryDataStream.Dispose();

    // Now lets use a nice dispose, etc...
    Bitmap2 image2;
    using (Stream originalBinaryDataStream2 = new MemoryStream(data))
    {
        image2 = new Bitmap(originalBinaryDataStream2);
    }

    image2.Save(@"C:\temp\pewpew.jpg"); // This throws the GDI+ exception.
}

誰かがストリームを閉じた状態で画像を保存する方法について何か提案はありますか?画像が保存された後にストリームを閉じることを忘れないように開発者に頼ることはできません。実際、開発者は、イメージがメモリストリームを使用して生成されたとは考えません(他のコードで発生するため)。

私は本当に混乱しています:(


1
別の質問で @HansPassantからこのコメントを受け取りました。コーデックがファイルの書き込みに問題がある場合は常に、この例外が発生します。追加する適切なデバッグステートメントは、Save()呼び出しの前のSystem.IO.File.WriteAllText(path、 "test")で、ファイルを作成する基本的な機能を確認します。あなたは今あなたが間違ったことを告げる良い例外を得るでしょう。
ファンカルロスオロペサ2017

usingブロック内でimage2.Saveを実行する必要があります。originalBinaryDataStream2 使用終了時に自動で廃棄されたと思います。そして、それは例外を投げるでしょう。
taynguyen

回答:


172

これはMemoryStreamであるため、ストリームを閉じる必要ありません。破棄しなくても問題はありませんが、使い捨てのものはすべて破棄することをお勧めします。(これについて詳しくは、この質問を参照してください。)

ただし、ビットマップを破棄する必要があります。これにより、ストリームが閉じます。基本的に、Bitmapコンストラクターにストリームを与えると、それはストリームを「所有」し、閉じるべきではありません。そのコンストラクタのドキュメントが言うように:

ビットマップの有効期間中、ストリームを開いたままにする必要があります。

ビットマップを破棄するときにストリームを閉じることを約束するドキュメントは見つかりませんが、かなり簡単に確認できるはずです。


2
驚くばかり!それは素晴らしい返事ですジョン。完璧な意味を作ります(そして、ドキュメントのストリームについて少し逃しました)。大満足!私はそれを
試してみたら

ルールCA2000に準拠したい場合、これについてどう対処するかについてのコメントはありますか?(msdn.microsoft.com/en-us/library/ms182289.aspx)
パトリックSzalapski

@Patrick:それは単に適用外です-あなたは基本的にリソースの所有権を譲渡しました。最も近いのは、Dispose呼び出しを無視する「NonClosingStream」ラッパーを作成することです。MiscUtilにあると思います-わかりません...
Jon Skeet

情報@ジョンをありがとう。私にとって、奇妙な理由で、ローカルの開発環境でdispose()を使用しても機能しましたが、本番環境では機能しませんでした。
オクソン2013年

92

GDI +で一般的なエラーが発生しました。不適切な保存パスが 原因である場合もあります。それに気づくのに半日かかった。したがって、画像を保存するためのパスも再確認したことを確認してください。


4
私はこれを見てうれしいです、私の道はでしたC\Users\mason\Desktop\pic.png。コロンがありません!気付かないうちに永遠に過ごしていただろう。
メイソン

4
また、画像を保存するフォルダが存在しないことも意味します。
Roemer 2016年

14

おそらく、C:\ Tempディレクトリが存在しない場合、ストリームがまだ存在していても、この例外がスローされることにも言及する価値があります。


+1この例外は、さまざまなシナリオで発生するようです。無効なパスは私が今日遭遇したものです。
カークブロードハースト

4

同じ問題がありましたが、実際の原因は、アプリケーションがCにファイルを保存する権限を持っていなかったためです。「D:\ ..」に変更すると、画像が保存されました。


2

ビットマップをコピーします。ビットマップの存続期間中、ストリームを開いたままにする必要があります。

画像を描画するとき:System.Runtime.InteropServices.ExternalException:GDIで一般的なエラーが発生しました

    public static Image ToImage(this byte[] bytes)
    {
        using (var stream = new MemoryStream(bytes))
        using (var image = Image.FromStream(stream, false, true))
        {
            return new Bitmap(image);
        }
    }

    [Test]
    public void ShouldCreateImageThatCanBeSavedWithoutOpenStream()
    {
        var imageBytes = File.ReadAllBytes("bitmap.bmp");

        var image = imageBytes.ToImage();

        image.Save("output.bmp");
    }

1
これは正確には機能しません。ToImage()のコードでは、ローカルの「画像」には元のファイル(jpegまたはpngなど)の.RawFormatが正しく含まれますが、ToImage()の戻り値には.RawFormat MemoryBmpが予期せず含まれます。
Patrick Szalapski、2011年

RawFormatしかし、それがどれほど重要かはわかりません。それを使用したい場合は、途中でオブジェクトから取得しますが、一般的には、実際に必要なタイプとして保存します
Nyerguds 2018年

2

ビットマップの別のコピーを作成してみることができます:

using (var memoryStream = new MemoryStream())
{
    // write to memory stream here

    memoryStream.Position = 0;
    using (var bitmap = new Bitmap(memoryStream))
    {
        var bitmap2 = new Bitmap(bitmap);
        return bitmap2;
    }
}

2

このエラーは、Citrixから試行したときに発生しました。サーバーで画像フォルダがC:\に設定されましたが、その権限がありません。画像フォルダを共有ドライブに移動すると、エラーは発生しなくなりました。


1

GDI +で一般的なエラーが発生しました。これは、画像の保存パスの問題が原因で発生する可能性があります。保存パスが長すぎるためにこのエラーが発生しました。まず画像を最短パスに保存し、長いパス処理テクニックを使用して正しい場所に移動することでこれを修正しました。


1

私が実行していた自動テストが、存在しないフォルダにスナップショットを保存しようとしたため、このエラーが発生しました。フォルダを作成した後、エラーは解決しました


0

私のコードを機能させる奇妙な解決策の1つ。画像をペイントで開き、同じ形式(.jpg)の新しいファイルとして保存します。この新しいファイルを試してみてください。ファイルが何らかの形で破損している可能性があることを明確に説明しています。これは、コードに他のすべてのバグが修正されている場合にのみ役立ちます


0

画像をパスに保存しようとしたときにも表示されました

C:\Program Files (x86)\some_directory

そして.exe、管理者として実行するように実行されなかった、私は、これはあまりにも同じ問題を持っている人を助けることを願っています。


0

私にとって、A generic error occurred in GDI+Saves to aの行で以下のコードがクラッシュしましたMemoryStream。コードはWebサーバーで実行されていましたが、サイトを実行していたアプリケーションプールを停止して開始することで解決しました。

GDI +の何らかの内部エラーであったに違いありません

    private static string GetThumbnailImageAsBase64String(string path)
    {
        if (path == null || !File.Exists(path))
        {
            var log = ContainerResolver.Container.GetInstance<ILog>();
            log.Info($"No file was found at path: {path}");
            return null;
        }

        var width = LibraryItemFileSettings.Instance.ThumbnailImageWidth;

        using (var image = Image.FromFile(path))
        {
            using (var thumbnail = image.GetThumbnailImage(width, width * image.Height / image.Width, null, IntPtr.Zero))
            {
                using (var memoryStream = new MemoryStream())
                {
                    thumbnail.Save(memoryStream, ImageFormat.Png); // <= crash here 
                    var bytes = new byte[memoryStream.Length];
                    memoryStream.Position = 0;
                    memoryStream.Read(bytes, 0, bytes.Length);
                    return Convert.ToBase64String(bytes, 0, bytes.Length);
                }
            }
        }
    }

0

WPFアプリで簡単な画像編集をしようとしたときに、このエラーに遭遇しました。

Image要素のSourceをビットマップに設定すると、ファイルを保存できなくなります。Source = nullに設定しても、ファイルは解放されないようです。

これで、イメージをSource of Image要素として使用することはないため、編集後に上書きできます。

編集

CacheOptionプロパティ(Thanks to @Nyerguds)について聞いた後、私は解決策を見つけました:したがって、ビットマップコンストラクターを使用する代わりに、設定後にUriを設定する必要がありCacheOption BitmapCacheOption.OnLoadます(Image1以下はWpf Image要素です)

の代わりに

Image1.Source = new BitmapImage(new Uri(filepath));

使用する:

var image = new BitmapImage();
image.BeginInit();
image.CreateOptions = BitmapCreateOptions.IgnoreImageCache;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = new Uri(filepath);
image.EndInit();
Image1.Source = image;

これを参照してください:WPF画像キャッシュ


1
WPF画像にはBitmapCacheOption.OnLoad、読み込み元から切断するための特定のパラメーターがあります。
Nyerguds 2018年

@Nyergudsに感謝します。あなたのコメントが正しい質問をすることに失敗するまで
mkb

0

このコードを試してください:

static void Main(string[] args)
{
    byte[] data = null;
    string fullPath = @"c:\testimage.jpg";

    using (MemoryStream ms = new MemoryStream())
    using (Bitmap tmp = (Bitmap)Bitmap.FromFile(fullPath))
    using (Bitmap bm = new Bitmap(tmp))
    {
        bm.SetResolution(96, 96);
        using (EncoderParameters eps = new EncoderParameters(1))
        {   
            eps.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
            bm.Save(ms, GetEncoderInfo("image/jpeg"), eps);
        }

        data = ms.ToArray();
    }

    File.WriteAllBytes(fullPath, data);
}

private static ImageCodecInfo GetEncoderInfo(string mimeType)
{
        ImageCodecInfo[] encoders = ImageCodecInfo.GetImageEncoders();

        for (int j = 0; j < encoders.Length; ++j)
        {
            if (String.Equals(encoders[j].MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase))
                return encoders[j];
        }
    return null;
}

0

画像プロセッサを使用して画像のサイズを変更したところ、「GDI +で一般的なエラーが発生しました」という例外が発生しました。

しばらく調べた後、アプリケーションプールとビンゴをリサイクルしてました。だから私はそれをここに書き留め、それが役に立てば幸い;)

乾杯

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.