GDI+中的Bitmap.Save方法发生了一般性错误。

87

我正在尝试上传并保存图像的缩略图到一个缩略图文件夹。

我使用了以下链接:

http://weblogs.asp.net/markmcdonnell/archive/2008/03/09/resize-image-before-uploading-to-server.aspx

但是

newBMP.Save(directory + "tn_" + filename);   

导致异常"A generic error occurred in GDI+"。

我尝试给文件夹授权,也尝试在保存时使用新的独立bmp对象。

编辑:

    protected void ResizeAndSave(PropBannerImage objPropBannerImage)
    {
        // Create a bitmap of the content of the fileUpload control in memory
        Bitmap originalBMP = new Bitmap(fuImage.FileContent);

        // Calculate the new image dimensions
        int origWidth = originalBMP.Width;
        int origHeight = originalBMP.Height;
        int sngRatio = origWidth / origHeight;
        int thumbWidth = 100;
        int thumbHeight = thumbWidth / sngRatio;

        int bannerWidth = 100;
        int bannerHeight = bannerWidth / sngRatio;

        // Create a new bitmap which will hold the previous resized bitmap
        Bitmap thumbBMP = new Bitmap(originalBMP, thumbWidth, thumbHeight);
        Bitmap bannerBMP = new Bitmap(originalBMP, bannerWidth, bannerHeight);

        // Create a graphic based on the new bitmap
        Graphics oGraphics = Graphics.FromImage(thumbBMP);
        // Set the properties for the new graphic file
        oGraphics.SmoothingMode = SmoothingMode.AntiAlias; oGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

        // Draw the new graphic based on the resized bitmap
        oGraphics.DrawImage(originalBMP, 0, 0, thumbWidth, thumbHeight);

        Bitmap newBitmap = new Bitmap(thumbBMP);
        thumbBMP.Dispose();
        thumbBMP = null;

        // Save the new graphic file to the server
        newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);

        oGraphics = Graphics.FromImage(bannerBMP);
        // Set the properties for the new graphic file
        oGraphics.SmoothingMode = SmoothingMode.AntiAlias; oGraphics.InterpolationMode = InterpolationMode.HighQualityBicubic;

        // Draw the new graphic based on the resized bitmap
        oGraphics.DrawImage(originalBMP, 0, 0, bannerWidth, bannerHeight);
        // Save the new graphic file to the server
        bannerBMP.Save("~/image/" + objPropBannerImage.ImageId + ".jpg");


        // Once finished with the bitmap objects, we deallocate them.
        originalBMP.Dispose();

        bannerBMP.Dispose();
        oGraphics.Dispose();
    }
18个回答

117
当从文件中构建Bitmap对象或Image对象时,该文件将在对象的生命周期内保持锁定状态。因此,您无法更改图像并将其保存回到原始文件中。
一个常见问题是:当尝试将图像保存到内存流时,会出现“GDI+异常”,解决方法是先将其保存到一个中间内存流中,然后再将其保存到目标内存流中。相关链接如下:
- http://support.microsoft.com/?id=814675 - A generic error occurred in GDI+, JPEG Image to MemoryStream - Image.Save(..) throws a GDI+ exception because the memory stream is closed - http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html 编辑:记得要先将图像保存到一个中间内存流中,然后再将其保存到目标内存流中。
    Bitmap newBitmap = new Bitmap(thumbBMP);
    thumbBMP.Dispose();
    thumbBMP = null;
    newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);

类似这样的:

string outputFileName = "...";
using (MemoryStream memory = new MemoryStream())
{
    using (FileStream fs = new FileStream(outputFileName, FileMode.Create, FileAccess.ReadWrite))
    {
        thumbBMP.Save(memory, ImageFormat.Jpeg);
        byte[] bytes = memory.ToArray();
        fs.Write(bytes, 0, bytes.Length);
    }
}

谢谢,使用这些方法我解决了问题。我将工作的代码再次发布。 - donstack
3
可能会帮助到别人的一个相关问题 - 我在打开位图时已经遇到了“GDI+ 中发生了一般性错误”问题,而且只是在远程 Web 服务器上,而不是在我的本地测试机器上。结果发现,Azure IIS 服务器上的 GDI+ 无法处理 GIMP 生成的新格式 BMP。我不得不选择:a)用 Paint 重新保存 BMP;b)改用未压缩的 PNG;c)用 GIMP 保存为24位 BMP(质量较差)。 - Andrew
我不确定在我正在尝试的项目中做错了什么......但出于某种原因,MemoryStream / FileStream组合对我有用(稍微变化以适应我的特定方法调用)。这是一个+1,为一个5年前的帖子。 - WernerCD
1
@WernerCD 我知道,这个问题(以及解决方法)有点违反直觉 - 但是在许多情况下(具体情况取决于具体情况),潜在的过度简化的解释是,您需要摆脱原始位图对象(及其引用),而内存流可以帮助实现这一点。 - NSGaga-mostly-inactive
@NSGaga 好吧,我之前用的是微软的(我从 PDF 中提取了一个文件到 Bitmap/Image 对象中)... 但是当我试图将该 Bitmap/Image 对象取出并放入磁盘时,它就崩溃了。我认为 Memory /FileStreambitmap.Save(...) 的使用方式有点反直觉,但这对我起到了作用。我想。无论如何,在困扰了我数小时后,这给了我所需要的推动。 - WernerCD

67

如果您传递给Bitmap.Save()的路径无效(文件夹不存在等),将显示此错误消息。


8
当然,这也是我的情况,我点赞了它,这样其他人就不会浪费更多时间。同时确保你的路径包括文件名。 - ahsant
对我来说,这个路径是存在的,但它没有足够的权限来写入文件夹。 - Gabriel Lopez

17
    // Once finished with the bitmap objects, we deallocate them.
    originalBMP.Dispose();

    bannerBMP.Dispose();
    oGraphics.Dispose();

这是一种编程风格,迟早会让你后悔。现在就有问题了,你忘了处理newBitmap。它会一直锁定文件,直到垃圾回收运行。如果垃圾回收没有运行,则第二次尝试保存到同一文件时会出现错误。GDI+异常太可怜了,无法提供良好的诊断,因此需要认真思考。除了成千上万的可搜索帖子提到这个错误之外。 始终优先使用using语句。即使代码抛出异常,它也不会忘记处理对象。
using (var newBitmap = new Bitmap(thumbBMP)) {
    newBitmap.Save("~/image/thumbs/" + "t" + objPropBannerImage.ImageId, ImageFormat.Jpeg);
}

尽管不太清楚你为什么要创建一个新的位图,但保存thumbBMP已经足够了。无论如何,将其余的可处理对象同样使用即可。

9
在我的情况下,位图图像文件已经存在于系统驱动器中,因此我的应用程序抛出了“GDI+ 中发生一般错误”的错误。
  1. 验证目标文件夹是否存在
  2. 验证目标文件夹中是否已经有同名文件

6

我在开发MVC应用程序时,保存文件时遇到了同样的问题:GDI+发生了一般性错误。这是因为我写错了保存图片的路径,我更正了保存路径,问题得到了解决。

img1.Save(Server.MapPath("/Upload/test.png", System.Drawing.Imaging.ImageFormat.Png);


--Above code need one change, as you need to put close brackets on Server.MapPath() method after writing its param.

像这样-

img1.Save(Server.MapPath("/Upload/test.png"), System.Drawing.Imaging.ImageFormat.Png);

6

请检查保存图片的文件夹权限

右键单击文件夹,然后选择:

属性 > 安全 > 编辑 > 添加-- 选择“每个人”,并勾选“完全控制”


这是一种可怕的做法,你永远不应该给予全局完全控制! - Dave C

4

由于以下原因,发生了GDI+异常:

  1. 文件夹访问问题
  2. 图像缺少属性

如果是文件夹问题-请为应用程序提供访问权限 如果缺少属性,则使用以下代码

代码1

using (Bitmap bmp = new Bitmap(webStream))
{
     using (Bitmap newImage = new Bitmap(bmp))
     {
         newImage.Save("c:\temp\test.jpg", ImageFormat.Jpeg);
     }
}

代码2

using (Bitmap bmp = new Bitmap(webStream))
{

     using (Bitmap newImage = new Bitmap(bmp))
     {
        newImage.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
        Rectangle lockedRect = new Rectangle(0, 0, bmp.Width, bmp.Height);
        BitmapData bmpData = newImage.LockBits(lockedRect, ImageLockMode.ReadWrite, bmp.PixelFormat);
        bmpData.PixelFormat = bmp.PixelFormat;
        newImage.UnlockBits(bmpData);
        using (Graphics gr = Graphics.FromImage(newImage))
         {
             gr.SmoothingMode = SmoothingMode.HighQuality;
             gr.InterpolationMode = InterpolationMode.HighQualityBicubic;
             gr.PixelOffsetMode = PixelOffsetMode.HighQuality;
         }

         foreach (var item in bmp.PropertyItems)
         {
             newImage.SetPropertyItem(item);
         }
         newImage.Save("c:\temp\test.jpg", ImageFormat.Jpeg);
    }
}

代码1和代码2的区别

代码-1:它只会创建图片并且可以在普通图像查看器中打开。

  • 该图片无法在Photoshop中打开。
  • 图像尺寸将增加一倍。

代码-2:使用此代码打开图像编辑工具。

使用代码1只会创建图像,但不会对其进行标记。


3

我经常检查/测试以下内容:

  • 路径+文件名是否包含给定文件系统的非法字符?
  • 文件是否已存在?(不好)
  • 路径是否已存在?(很好)
  • 如果路径是相对的:我是否期望它在正确的父目录中(大多数情况下是bin/Debug;-))?
  • 程序是否以可写方式运行,以及以哪个用户身份运行?(服务可能会有些棘手!)
  • 完整路径是否真的不包含非法字符?(某些Unicode字符接近不可见)

除了这个列表,我从来没有遇到过使用Bitmap.Save()出现任何问题。


2

我遇到了相同的异常问题,不过是由其他原因造成的。

简而言之:

确保在调用.Save方法之前,Bitmap对象的Stream属性没有被释放。

完整故事:

有一个方法返回一个Bitmap对象,它是通过以下方式从MemoryStream中构建的:

private Bitmap getImage(byte[] imageBinaryData){
    .
    .
    .
    Bitmap image;
    using (var stream = new MemoryStream(imageBinaryData))
    {
        image = new Bitmap(stream);
    }
    return image;
}

然后有人使用返回的图像将其保存为文件。
image.Save(path);

问题在于当尝试保存图片时,原始流已经被处理掉了,导致GDI+异常。解决这个问题的方法是返回Bitmap对象而不是处理掉流本身。请参考以下代码:
```Bitmap返回 = new Bitmap(流);```
而不是
```流.Dispose(); return new Bitmap(流);```
private Bitmap getImage(byte[] imageBinaryData){
   .
   .
   .
   Bitmap image;
   var stream = new MemoryStream(imageBinaryData))
   image = new Bitmap(stream);

   return image;
}

然后:

using (var image = getImage(binData))
{
   image.Save(path);
}

这是一个很好的答案,可惜它被许多与错误路径相关的重复答案所掩盖。底层流无法被销毁。另一种方法是从旧位图创建一个新位图:new Bitmap(oldBitmap).Save(...) - Corey Alix

1
我使用FileStream使其工作,并从以下链接获取帮助:
http://alperguc.blogspot.in/2008/11/c-generic-error-occurred-in-gdi.html http://csharpdotnetfreak.blogspot.com/2010/02/resize-image-upload-ms-sql-database.html
System.Drawing.Image imageToBeResized = System.Drawing.Image.FromStream(fuImage.PostedFile.InputStream);
        int imageHeight = imageToBeResized.Height;
        int imageWidth = imageToBeResized.Width;
        int maxHeight = 240;
        int maxWidth = 320;
        imageHeight = (imageHeight * maxWidth) / imageWidth;
        imageWidth = maxWidth;

        if (imageHeight > maxHeight)
        {
            imageWidth = (imageWidth * maxHeight) / imageHeight;
            imageHeight = maxHeight;
        }

        Bitmap bitmap = new Bitmap(imageToBeResized, imageWidth, imageHeight);
        System.IO.MemoryStream stream = new MemoryStream();
        bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Jpeg);
        stream.Position = 0;
        byte[] image = new byte[stream.Length + 1];
        stream.Read(image, 0, image.Length);
        System.IO.FileStream fs
= new System.IO.FileStream(Server.MapPath("~/image/a.jpg"), System.IO.FileMode.Create
, System.IO.FileAccess.ReadWrite);
            fs.Write(image, 0, image.Length);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接