Image.Save (..) melontarkan pengecualian GDI + karena aliran memori ditutup

108

Saya punya beberapa data biner yang ingin saya simpan sebagai gambar. Ketika saya mencoba untuk menyimpan gambar, itu melontarkan pengecualian jika aliran memori yang digunakan untuk membuat gambar, ditutup sebelum penyimpanan. Alasan saya melakukan ini adalah karena saya secara dinamis membuat gambar dan karenanya .. saya perlu menggunakan aliran memori.

ini kodenya:

[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.
}

Adakah yang punya saran tentang bagaimana saya bisa menyimpan gambar dengan aliran ditutup? Saya tidak dapat mengandalkan pengembang untuk mengingat untuk menutup aliran setelah gambar disimpan. Faktanya, pengembang TIDAK ADA IDEA bahwa gambar dibuat menggunakan aliran memori (karena ini terjadi di beberapa kode lain, di tempat lain).

Saya benar-benar bingung :(

Pure.Krome
sumber
1
Saya mendapat komentar ini dari @HansPassant di pertanyaan lain . Anda akan mendapatkan pengecualian ini setiap kali codec mengalami masalah saat menulis file. Pernyataan debugging yang baik untuk ditambahkan adalah System.IO.File.WriteAllText (path, "test") sebelum panggilan Save (), ini memverifikasi kemampuan dasar untuk membuat file. Sekarang Anda akan mendapatkan pengecualian bagus yang memberi tahu Anda kesalahan apa yang Anda lakukan.
Juan Carlos Oropeza
Anda harus image2.Save di dalam usingblok. Saya pikir originalBinaryDataStream2 secara otomatis dibuang pada akhir penggunaan. Dan itu akan membuang pengecualian.
taynguyen

Jawaban:

172

Karena ini adalah MemoryStream, Anda benar-benar tidak perlu menutup streaming - tidak ada hal buruk yang akan terjadi jika Anda tidak melakukannya, meskipun jelas merupakan praktik yang baik untuk membuang apa pun yang dapat dibuang. (Lihat pertanyaan ini untuk lebih lanjut tentang ini.)

Namun, Anda harus membuang Bitmap - dan itu akan menutup aliran untuk Anda. Pada dasarnya sekali Anda memberi konstruktor Bitmap sebuah aliran, itu "memiliki" aliran tersebut dan Anda tidak boleh menutupnya. Seperti yang dikatakan oleh dokumen untuk konstruktor itu :

Anda harus membiarkan streaming tetap terbuka selama Bitmap aktif.

Saya tidak dapat menemukan dokumen apa pun yang menjanjikan untuk menutup streaming saat Anda membuang bitmap, tetapi Anda harus dapat memverifikasinya dengan cukup mudah.

Jon Skeet
sumber
2
luar biasa! itu jawaban yang bagus Jon. Sangat masuk akal (dan saya melewatkan sedikit tentang streaming di dokumen). Dua jempol! Saya akan melaporkan kembali setelah saya mencobanya :)
Pure.Krome
Ada komentar tentang bagaimana melakukan hal ini jika kita ingin mematuhi aturan CA2000? (msdn.microsoft.com/en-us/library/ms182289.aspx)
Patrick Szalapski
@Patrick: Ini sama sekali tidak berlaku - Anda telah mentransfer kepemilikan sumber daya, pada dasarnya. Cara terdekat yang bisa Anda lakukan adalah membuat pembungkus "NonClosingStream" yang mengabaikan panggilan Buang. Saya pikir saya mungkin punya satu di MiscUtil - tidak yakin ...
Jon Skeet
Terima kasih atas info @Jon. Bagi saya, untuk beberapa alasan aneh itu bekerja bahkan dengan pembuangan () di lingkungan pengembang lokal tetapi tidak berfungsi dalam produksi.
Oxon
92

Kesalahan umum terjadi di GDI +. Mungkin juga akibat dari jalur penyimpanan yang salah ! Butuh waktu setengah hari untuk menyadarinya. Jadi pastikan Anda telah memeriksa ulang jalur untuk menyimpan gambar juga.

Houman
sumber
4
Saya senang saya melihat ini, jalan saya C\Users\mason\Desktop\pic.png. Usus besar hilang! Saya akan menghabiskan selamanya sebelum saya menyadarinya.
tukang batu
4
Salah juga berarti bahwa folder tempat Anda ingin menyimpan gambar tidak ada.
Roemer
14

Mungkin perlu disebutkan bahwa jika direktori C: \ Temp tidak ada, pengecualian ini juga akan muncul meskipun streaming Anda masih ada.

Rojzik
sumber
+1 Pengecualian ini tampaknya terjadi di berbagai skenario. Jalan yang tidak valid adalah yang saya temui hari ini.
Kirk Broadhurst
4

Saya memiliki masalah yang sama tetapi sebenarnya penyebabnya adalah aplikasi tidak memiliki izin untuk menyimpan file di C. Ketika saya mengubah ke "D: \ .." gambar telah disimpan.

Morad Aktam
sumber
2

Salin Bitmap. Anda harus menjaga aliran tetap terbuka selama bitmap berlangsung.

Saat menggambar gambar: System.Runtime.InteropServices.ExternalException: Terjadi kesalahan umum di 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");
    }
Brian Low
sumber
1
Ini tidak bekerja dengan tepat; dalam kode Anda di ToImage (), "image" lokal akan memiliki .RawFormat dengan benar dari apa pun file aslinya (jpeg atau png, dll), sedangkan nilai kembalian ToImage () secara tidak terduga akan memiliki .RawFormat MemoryBmp.
Patrick Szalapski
Namun, tidak yakin seberapa RawFormatpenting. Jika Anda ingin menggunakan itu, mengambilnya dari suatu tempat objek di sepanjang jalan, tetapi secara umum, save as type apa pun yang Anda benar-benar ingin memiliki .
Nyerguds
2

Anda dapat mencoba membuat salinan bitmap lain:

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;
    }
}
Yuri Perekupko
sumber
2

Kesalahan ini terjadi pada saya ketika saya mencoba dari Citrix. Folder gambar disetel ke C: \ di server, yang saya tidak memiliki hak istimewa. Setelah folder gambar dipindahkan ke drive bersama, kesalahan itu hilang.

Jay K
sumber
1

Kesalahan umum terjadi di GDI +. Ini dapat terjadi karena masalah jalur penyimpanan gambar, saya mendapat kesalahan ini karena jalur penyimpanan saya terlalu panjang, saya memperbaikinya dengan terlebih dahulu menyimpan gambar di jalur terpendek dan memindahkannya ke lokasi yang benar dengan teknik penanganan jalur panjang.

S.Roshanth
sumber
1

Saya mendapatkan kesalahan ini, karena pengujian otomatis yang saya jalankan, mencoba menyimpan snapshot ke dalam folder yang tidak ada. Setelah saya membuat folder, kesalahan teratasi

P. Lisa
sumber
0

Satu solusi aneh yang membuat kode saya berfungsi. Buka gambar di paint dan simpan sebagai file baru dengan format yang sama (.jpg). Sekarang coba dengan file baru ini dan berhasil. Ini dengan jelas menjelaskan kepada Anda bahwa file tersebut mungkin saja rusak. Ini hanya dapat membantu jika kode Anda memperbaiki semua bug lainnya

Vinothkumar
sumber
0

Itu juga muncul bersama saya ketika saya mencoba menyimpan gambar ke jalur

C:\Program Files (x86)\some_directory

dan .exetidak dijalankan untuk dijalankan sebagai administrator, saya harap ini dapat membantu seseorang yang memiliki masalah yang sama juga.

Ali Ezzat Odeh
sumber
0

Bagi saya kode di bawah ini crash A generic error occurred in GDI+pada baris yang Menyimpan ke a MemoryStream. Kode berjalan di server web dan saya menyelesaikannya dengan menghentikan dan memulai Application Pool yang menjalankan situs.

Pasti ada beberapa kesalahan internal di 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);
                }
            }
        }
    }
mortb
sumber
0

Saya menemukan kesalahan ini ketika saya mencoba pengeditan gambar sederhana di aplikasi WPF.

Menyetel Sumber elemen Gambar ke bitmap mencegah penyimpanan file. Bahkan pengaturan Source = null sepertinya tidak merilis file.

Sekarang saya tidak pernah menggunakan gambar sebagai elemen Sumber Gambar, jadi saya bisa menimpa setelah mengedit!

EDIT

Setelah mendengar tentang properti CacheOption (Terima kasih kepada @Nyerguds) saya menemukan solusinya: Jadi, alih-alih menggunakan konstruktor Bitmap, saya harus mengatur Uri setelah pengaturan CacheOption BitmapCacheOption.OnLoad. (Di Image1bawah ini adalah Imageelemen Wpf )

Dari pada

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

Menggunakan:

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

Lihat ini: Cache Gambar WPF

mkb
sumber
1
Gambar WPF memiliki parameter khusus BitmapCacheOption.OnLoaduntuk memutuskan sambungannya dari sumber pemuatan.
Nyerguds
Terima kasih @Nyerguds, sampai komentar Anda saya gagal mengajukan pertanyaan yang benar
mkb
0

Coba kode ini:

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;
}
BogdanRB
sumber
0

Saya menggunakan imageprocessor untuk mengubah ukuran gambar dan suatu hari saya mendapat pengecualian "Terjadi kesalahan umum di GDI +".

Setelah mendongak beberapa saat saya mencoba untuk mendaur ulang kumpulan aplikasi dan bingo itu berfungsi. Jadi saya catat di sini, semoga membantu;)

Bersulang

Hoàng Nghĩa
sumber