Image.Save (..) lanza una excepción GDI + porque el flujo de memoria está cerrado

108

Tengo algunos datos binarios que quiero guardar como imagen. Cuando intento guardar la imagen, arroja una excepción si el flujo de memoria utilizado para crear la imagen se cerró antes de guardar. La razón por la que hago esto es porque estoy creando imágenes dinámicamente y como tal ... necesito usar un flujo de memoria.

este es el código:

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

¿Alguien tiene alguna sugerencia sobre cómo podría guardar una imagen con la transmisión cerrada? No puedo confiar en que los desarrolladores recuerden cerrar la transmisión después de guardar la imagen. De hecho, el desarrollador NO tendría NINGUNA IDEA de que la imagen se generó usando un flujo de memoria (porque sucede en algún otro código, en otro lugar).

Estoy realmente confundido :(

Pure.Krome
fuente
1
Recibí este comentario de @HansPassant en otra pregunta . Obtendrá esta excepción siempre que el códec tenga problemas para escribir el archivo. Una buena declaración de depuración para agregar es System.IO.File.WriteAllText (ruta, "prueba") antes de la llamada Save (), verifica la capacidad básica para crear el archivo. Ahora obtendrá una buena excepción que le dice qué hizo mal.
Juan Carlos Oropeza
Deberías image2.Save dentro del usingbloque. Creo que originalBinaryDataStream2 se eliminó automáticamente al final del uso. Y eso arrojaría la excepción.
taynguyen

Respuestas:

172

Como es un MemoryStream, realmente no es necesario cerrar el flujo; si no lo hace, no pasará nada malo, aunque obviamente es una buena práctica desechar todo lo que sea desechable de todos modos. (Consulte esta pregunta para obtener más información).

Sin embargo, debería desechar el mapa de bits, y eso cerrará la transmisión por usted. Básicamente, una vez que le da un flujo al constructor de mapa de bits, este "posee" el flujo y no debe cerrarlo. Como dicen los documentos de ese constructor :

Debe mantener la transmisión abierta durante la vida útil del mapa de bits.

No puedo encontrar ningún documento que prometa cerrar la transmisión cuando deseche el mapa de bits, pero debería poder verificarlo con bastante facilidad.

Jon Skeet
fuente
2
¡increíble! Esa es una gran respuesta Jon. Tiene un sentido perfecto (y me perdí la parte de la transmisión en los documentos). ¡Dos pulgares para arriba! Informaré cuando lo haya
intentado
¿Algún comentario sobre cómo hacer esto si queremos obedecer la regla CA2000? (msdn.microsoft.com/en-us/library/ms182289.aspx)
Patrick Szalapski
@Patrick: Simplemente no es aplicable, básicamente has transferido la propiedad del recurso. Lo más cerca que podría llegar sería crear un contenedor "NonClosingStream" que ignore la llamada Dispose. Creo que puedo tener uno en MiscUtil, no estoy seguro ...
Jon Skeet
Gracias por la información @Jon. Para mí, por alguna extraña razón, estaba funcionando incluso con dispose () en el entorno de desarrollo local, pero no funcionó en producción.
Oxon
92

Se produjo un error genérico en GDI +. ¡También puede resultar de una ruta de guardado incorrecta ! Me tomó medio día darme cuenta de eso. Así que asegúrese de haber verificado dos veces la ruta para guardar la imagen también.

Houman
fuente
4
Me alegro de haber visto esto, mi camino fue C\Users\mason\Desktop\pic.png. ¡Falta el colon! Hubiera pasado una eternidad antes de darme cuenta de eso.
albañil
4
Incorrecto también significa que la carpeta en la que desea guardar la imagen no existe.
Roemer
14

Quizás vale la pena mencionar que si el directorio C: \ Temp no existe, también lanzará esta excepción incluso si su flujo aún existe.

Rojzik
fuente
+1 Esta excepción parece ocurrir en una variedad de escenarios. La ruta no válida es una que encontré hoy.
Kirk Broadhurst
4

Tuve el mismo problema, pero en realidad la causa fue que la aplicación no tenía permiso para guardar archivos en C. Cuando cambié a "D: \ .." la imagen se guardó.

Morad Aktam
fuente
2

Copie el mapa de bits. Debe mantener la transmisión abierta durante la vida útil del mapa de bits.

Al dibujar una imagen: System.Runtime.InteropServices.ExternalException: se produjo un error genérico en 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
fuente
1
Esto no funciona exactamente; en su código en ToImage (), la "imagen" local tendrá correctamente un .RawFormat de lo que sea el archivo original (jpeg o png, etc.), mientras que el valor de retorno de ToImage () tendrá inesperadamente .RawFormat MemoryBmp.
Patrick Szalapski
Sin RawFormatembargo, no estoy seguro de cuánto importa. Si desea usar eso, recupérelo del objeto en algún lugar del camino, pero en general, guárdelo como el tipo que realmente desee tener .
Nyerguds
2

Puede intentar crear otra copia del mapa de bits:

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
fuente
2

Se me ocurrió este error cuando intentaba desde Citrix. La carpeta de imágenes se configuró en C: \ en el servidor, para lo cual no tengo privilegios. Una vez que la carpeta de imágenes se movió a una unidad compartida, el error desapareció.

Jay K
fuente
1

Se produjo un error genérico en GDI +. Puede ocurrir debido a problemas de rutas de almacenamiento de imágenes, recibí este error porque mi ruta de almacenamiento es demasiado larga, lo solucioné almacenando primero la imagen en la ruta más corta y la moví a la ubicación correcta con técnicas de manejo de rutas largas.

S.Roshanth
fuente
1

Recibía este error porque la prueba automatizada que estaba ejecutando intentaba almacenar instantáneas en una carpeta que no existía. Después de que creé la carpeta, el error se resolvió

P. Lisa
fuente
0

Una extraña solución que hizo que mi código funcionara. Abra la imagen en paint y guárdela como un nuevo archivo con el mismo formato (.jpg). Ahora intente con este nuevo archivo y funciona. Le explica claramente que el archivo podría estar dañado de alguna manera. Esto puede ayudar solo si su código tiene todos los demás errores corregidos

Vinothkumar
fuente
0

También me ha aparecido cuando intentaba guardar una imagen en la ruta.

C:\Program Files (x86)\some_directory

y .exeno se ejecutó para ejecutarse como administrador, espero que esto pueda ayudar a alguien que también tenga el mismo problema.

Ali Ezzat Odeh
fuente
0

Para mí, el código a continuación se bloqueó A generic error occurred in GDI+en la línea que se guarda en un archivo MemoryStream. El código se estaba ejecutando en un servidor web y lo resolví deteniendo e iniciando el grupo de aplicaciones que estaba ejecutando el sitio.

Debe haber sido un error interno en 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
fuente
0

Encontré este error cuando intentaba editar una imagen simple en una aplicación WPF.

Establecer el origen de un elemento de imagen en el mapa de bits evita que se guarde el archivo. Incluso establecer Source = null no parece liberar el archivo.

Ahora simplemente nunca uso la imagen como el elemento Fuente de la imagen, ¡así que puedo sobrescribir después de editar!

EDITAR

Después de escuchar sobre la propiedad CacheOption (Gracias a @Nyerguds) encontré la solución: Entonces, en lugar de usar el constructor de mapa de bits, debo configurar el Uri después de configurarlo CacheOption BitmapCacheOption.OnLoad(a Image1continuación se muestra el Imageelemento Wpf )

En vez de

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

Utilizar:

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

Vea esto: Almacenamiento en caché de imágenes de WPF

mkb
fuente
1
Las imágenes de WPF tienen un parámetro específico BitmapCacheOption.OnLoadpara desconectarlas de la fuente de carga.
Nyerguds
Gracias @Nyerguds, hasta su comentario no pude hacer las preguntas correctas
mkb
0

Prueba este código:

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
fuente
0

Usé el procesador de imágenes para cambiar el tamaño de las imágenes y un día obtuve la excepción "Se produjo un error genérico en GDI +".

Después de buscar un rato traté de reciclar el grupo de aplicaciones y bingo funciona. Así que lo anoto aquí, espero que ayude;)

Salud

Hoàng Nghĩa
fuente