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”),它验证了创建文件的基本能力。现在,您将获得一个很好的例外,告诉您您做错了什么。
Juan Carlos Oropeza

您应该image2.Save内部using块。我认为会originalBinaryDataStream2 在使用结束时自动处置。这将引发异常。
taynguyen

Answers:


172

因为它是MemoryStream,所以您实际上不需要关闭流-如果不这样做,则不会发生任何不好的事情,尽管显然,处置任何可丢弃的东西都是一种好习惯。(有关更多信息,请参见此问题。)

但是,您应该处理位图-这样将关闭流。基本上,一旦给Bitmap构造函数一个流,它就“拥有”该流,并且您不应该关闭它。正如该构造函数的文档所说:

您必须在位图的生存期内保持流打开。

放置位图时,我找不到任何承诺关闭流的文档,但是您应该能够轻松地进行验证。


2
太棒了!乔恩,这是一个很好的答复。做出完美的感觉(我错过了有关文档流的内容)。太棒了!我会给我报告的时间:)
Pure.Krome

如果我们要遵守规则CA2000,对此有何评论?(msdn.microsoft.com/en-us/library/ms182289.aspx)
帕特里克Szalapski

@Patrick:这根本不适用-基本上,您已经转移了资源的所有权。最接近的可能是创建一个“ NonClosingStream”包装程序,该包装程序将忽略Dispose调用。我想我可能在MiscUtil中有一个-不确定...
Jon Skeet

感谢您的信息@Jon。对我来说,出于某种奇怪的原因,即使在本地开发人员环境中使用dispose()也可以工作,但在生产环境中却无法工作。
奥克森

92

GDI +中发生一般错误。错误的保存路径 也可能导致!花了我半天时间注意到这一点。因此,请确保您已仔细检查了保存图像的路径。


4
我很高兴看到这个,我的路是C\Users\mason\Desktop\pic.png。缺少冒号!我会花一辈子的时间才意识到这一点。
梅森2015年

4
错误还表示您不希望将图像保存到的文件夹。
罗默(Roemer)

14

也许值得一提的是,如果C:\ Temp目录不存在,即使您的流仍然存在,它也会引发此异常。


+1在各种情况下似乎都会发生此异常。无效路径是我今天遇到的路径。
Kirk Broadhurst,


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

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

一种奇怪的解决方案使我的代码正常工作。在画图中打开图像并将其另存为具有相同格式(.jpg)的新文件。现在尝试使用这个新文件,它可以工作。它清楚地向您说明该文件可能以某种方式被破坏。仅当您的代码中修复了所有其他错误时,这才有帮助


0

当我尝试将图像保存到路径时,它也与我一起出现

C:\Program Files (x86)\some_directory

.exe没有以管理员身份运行,我希望这也可以对遇到相同问题的人有所帮助。


0

对我来说,以下代码在A generic error occurred in GDI+保存到的行上崩溃了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似乎也不会释放文件。

现在,我只是从未将图像用作“图像来源”元素,因此编辑后可以覆盖!

编辑

听到CacheOption属性(感谢@Nyerguds)后,我找到了解决方案:因此,除了使用Bitmap构造函数外,我还必须在设置后设置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

谢谢@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

我使用imageprocessor调整图像大小,有一天我收到“ GDI +中发生一般错误”异常。

经过一会儿的查找,我尝试回收应用程序池并进行宾果游戏。所以我在这里注意,希望对您有所帮助;)

干杯

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.