【问题标题】:A generic error occurred in GDI+, JPEG Image to MemoryStreamGDI+、JPEG 图像到 MemoryStream 中发生一般错误
【发布时间】:2010-11-06 09:16:13
【问题描述】:

这似乎是整个网络上一个臭名昭著的错误。如此之多以至于我无法找到我的问题的答案,因为我的场景不适合。将图像保存到流时引发异常。

奇怪的是,这与 png 完美配合,但在 jpg 和 gif 上会出现上述错误,这相当令人困惑。

最类似的问题与将图像保存到没有权限的文件有关。具有讽刺意味的是,解决方案是像我一样使用内存流......

public static byte[] ConvertImageToByteArray(Image imageToConvert)
{
    using (var ms = new MemoryStream())
    {
        ImageFormat format;
        switch (imageToConvert.MimeType())
        {
            case "image/png":
                format = ImageFormat.Png;
                break;
            case "image/gif":
                format = ImageFormat.Gif;
                break;
            default:
                format = ImageFormat.Jpeg;
                break;
        }

        imageToConvert.Save(ms, format);
        return ms.ToArray();
    }
}

有关异常的更多详细信息。造成这么多问题的原因是缺乏解释:(

System.Runtime.InteropServices.ExternalException was unhandled by user code
Message="A generic error occurred in GDI+."
Source="System.Drawing"
ErrorCode=-2147467259
StackTrace:
   at System.Drawing.Image.Save(Stream stream, ImageCodecInfo encoder, EncoderParameters    encoderParams)
   at System.Drawing.Image.Save(Stream stream, ImageFormat format)
   at Caldoo.Infrastructure.PhotoEditor.ConvertImageToByteArray(Image imageToConvert) in C:\Users\Ian\SVN\Caldoo\Caldoo.Coordinator\PhotoEditor.cs:line 139
   at Caldoo.Web.Controllers.PictureController.Croppable() in C:\Users\Ian\SVN\Caldoo\Caldoo.Web\Controllers\PictureController.cs:line 132
   at lambda_method(ExecutionScope , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassa.<InvokeActionMethodWithFilters>b__7()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)
 InnerException: 

到目前为止我已经尝试过好的事情。

  1. 正在克隆图像并进行处理。
  2. 通过 jpeg 质量设置检索该 MIME 的编码器。

【问题讨论】:

标签: c# gdi+


【解决方案1】:

好吧,我似乎只是靠运气找到了原因,并且该特定方法没有任何问题,它进一步备份了调用堆栈。

之前我调整了图像的大小,作为该方法的一部分,我返回调整后的对象,如下所示。我已经插入了两个对上述方法的调用和一个直接保存到文件的方法。

// At this point the new bitmap has no MimeType
// Need to output to memory stream
using (var m = new MemoryStream())
{
       dst.Save(m, format);

       var img = Image.FromStream(m);

       //TEST
       img.Save("C:\\test.jpg");
       var bytes = PhotoEditor.ConvertImageToByteArray(img);


       return img;
 }

在保存对象时,创建对象的内存流必须打开。我不确定这是为什么。有没有人能够启发我以及我如何解决这个问题。

我只从流中返回,因为在使用类似于this 的调整大小代码后,目标文件具有未知的 mime 类型 (img.RawFormat.Guid),并且 Id 喜欢 Mime 类型在所有图像对象上都是正确的否则很难编写通用处理代码。

编辑

这并没有出现在我最初的搜索中,而是 here's Jon Skeet 的答案

【讨论】:

  • 我没有意识到当您从内存流中获取位图时,您不应该关闭该流。很有帮助,谢谢
  • 谢谢。这可能挽救了我的最后一根头发。
  • 谢谢!这节省了我很多时间,但有一件事,你介意在你的答案开始时强调错误的原因,因为我(我猜大多数人)在最初浏览答案时错过了它,也许像“如果您打算再次使用图像,请不要关闭内存流”会很棒;D
  • 你的“dst”变量是什么?
  • @madcapnmckay 请解释一下“dst”变量是什么及其意义
【解决方案2】:

如果您收到该错误,那么我可以说您的应用程序对某个目录没有写入权限。

例如,如果您尝试将图像从内存流保存到文件系统,您可能会收到该错误。

如果您使用的是 XP,请确保为该文件夹中的 aspnet 帐户添加写入权限。

如果您使用的是 windows server (2003,2008) 或 Vista,请确保为网络服务帐户添加写入权限。

希望对某人有所帮助。

【讨论】:

  • 你没有!我在该死的写权限上浪费了 2 个小时......来到这里发布这个。希望大家多多点赞。 :)
  • 这是我的解决方案。完全 +1!
  • 您可以在保存位图之前先执行 File.WriteAllText("filename.jpg", "") 然后 File.DeleteFile("filename.jpg")。在我的基准测试中,这只需要 0.001 秒,你会得到一个很好的“你没有权限在那里保存 filename.jpg”
  • 在我的情况下,该目录不存在。
  • 我正在更改权限,当我发现它是 FILE-IN-USE 时,@Jonathan 也有。创建图像的程序还没有释放锁。我最终保存到一个新文件,而不是重写原始文件。
【解决方案3】:

我也会添加这个错误原因,希望它可以帮助一些未来的互联网旅行者。 :)

GDI+ 将图像的最大高度限制为 65500

我们会进行一些基本的图像调整,但在调整大小时,我们会尽量保持纵横比。我们有一个 QA 人,他对这份工作有点太擅长了;他决定用一张 480 像素高的 1 像素宽的照片来测试这一点。当图像被缩放以符合我们的尺寸时,高度为 68,000 像素以北,我们的应用以A generic error occurred in GDI+ 爆炸。

您可以通过测试自己验证这一点:

  int width = 480;
  var height = UInt16.MaxValue - 36; //succeeds at 65499, 65500
  try
  {
    while(true)
    {
      var image = new Bitmap(width, height);
      using(MemoryStream ms = new MemoryStream())
      {
        //error will throw from here
        image.Save(ms, ImageFormat.Jpeg);
      }
      height += 1;
    }
  }
  catch(Exception ex)
  {
    //explodes at 65501 with "A generic error occurred in GDI+."
  }

可惜Bitmap的构造函数中没有一个友好的.net ArgumentException

【讨论】:

  • 谢谢您 - 这位互联网时间旅行者非常感谢您留下此消息。
  • 根据我的测试,65535 实际上是最大值。在 65536 处,我开始看到一般错误。
  • 刚刚再试一次:Win10 .net 4.5 和 .net 4.6.1,它在 65501 处爆炸,这似乎更加随机。代码也充满语法错误,将更新:)
【解决方案4】:

这篇文章详细解释了到底发生了什么:Bitmap and Image constructor dependencies

简而言之,在从 构造的 Image 的生命周期内,不得销毁流。

所以,而不是

using (var strm = new ... )  {
    myImage = Image.FromStream(strm);
}

试试这个

Stream imageStream;
...

    imageStream = new ...;
    myImage = Image.FromStream(strm);

并在表单关闭或网页关闭时关闭 imageStream

【讨论】:

  • 是的,这个得到了我。我很认真,将我的流包装在 using 中,后来尝试将图像复制到内存流中并收到可怕的“GDI+ 中的通用错误”消息。
  • 你的链接给了我无限的重定向; this one 有效。我在保存PixelFormat.Format32bppArgb 但不是PixelFormat.Format1bppIndexed 时遇到问题。您链接的文章解释了原因:GDI+ 可能会选择从源流中重新解码位图数据,而不是将所有内容都保存在内存中。我的猜测是它不会重新解码 1bpp 图像。
  • 即使是新链接也不再起作用了。一个简单的谷歌搜索似乎没有显示正确的页面。但我很高兴找到这个答案!由于this issue,我通过复制到新位图的解决方法失败了
【解决方案5】:

如果您尝试保存到无效路径或存在权限问题,您也会收到此异常。

如果您不能 100% 确定文件路径是否可用且权限是否正确,请尝试将 a 写入文本文件。只需几秒钟即可排除非常简单的修复方法。

var img = System.Drawing.Image.FromStream(incomingStream);

// img.Save(path);
System.IO.File.WriteAllText(path, "Testing valid path & permissions.");

别忘了清理你的文件。

【讨论】:

  • 这对我来说是个问题...我希望错误不那么模糊,这样可以节省我很多时间。
  • 是的!您要保存到的文件夹必须存在。我现在先检查一下,然后再尝试保存图像。 (不过,这个错误还是让我发现了,大约一年一次。)
  • 我的路径是一个目录,而不是一个文件。
【解决方案6】:

将图像保存到位图变量

using (var ms = new MemoryStream())
{
    Bitmap bmp = new Bitmap(imageToConvert);
    bmp.Save(ms, format);
    return ms.ToArray();
}

【讨论】:

  • 这解决了我的问题。你能解释一下为什么将图像保存到位图会吓跑异常吗?
  • 拯救了我的一天..不知道是什么导致了问题,但位图保存工作.. System.Drawing.Image 不会保存到内存流,但位图会!!!
  • 这对我来说是最好的解决方案。创建新的位图并从中转换。
  • 我知道这篇文章已经过时了,但我想让你知道这让我免于把头发都扯掉!大声笑
【解决方案7】:

以防万一有人像我一样做愚蠢的事情。 1.确保路径确实存在。 2.确保你有写权限。 3.确保您的路径正确,在我的情况下,我在 TargetPath 中缺少文件名:(

应该说,您的路径比“GDI+ 中发生一般错误”更糟糕

【讨论】:

    【解决方案8】:

    我在保存 JPEG 时也遇到了这个错误,但仅限于某些图像。

    我的最终代码:

      try
      {
        img.SaveJpeg(tmpFile, quality); // This is always successful for say image1.jpg, but always throws the GDI+ exception for image2.jpg
      }
      catch (Exception ex)
      {
        // Try HU's method: Convert it to a Bitmap first
        img = new Bitmap(img); 
        img.SaveJpeg(tmpFile, quality); // This is always successful
      }
    

    我没有创建图像,所以我不知道有什么区别。
    如果有人能解释一下,我将不胜感激。

    这是我的 SaveJpeg 函数,仅供参考:

    private static void SaveJpeg(this Image img, string filename, int quality)
    {
      EncoderParameter qualityParam = new EncoderParameter(Encoder.Quality, (long)quality);
      ImageCodecInfo jpegCodec = GetEncoderInfo("image/jpeg");
      EncoderParameters encoderParams = new EncoderParameters(1);
      encoderParams.Param[0] = qualityParam;
      img.Save(filename, jpegCodec, encoderParams);
    }
    
    private static ImageCodecInfo GetEncoderInfo(string mimeType)
    {
        var encoders = ImageCodecInfo.GetImageEncoders();
        var encoder = encoders.SingleOrDefault(c => string.Equals(c.MimeType, mimeType, StringComparison.InvariantCultureIgnoreCase));
        if (encoder == null) throw new Exception($"Encoder not found for mime type {mimeType}");
        return encoder;
    }
    

    【讨论】:

    • 这解决了拉扯头发的日子。这是我认为我写过的最 wtf 的代码 :)
    【解决方案9】:

    有一个非常相似的问题,还尝试克隆不起作用的图像。我发现最好的解决方案是从内存流中加载的图像创建一个新的 Bitmap 对象。这样可以处理流,例如

    using (var m = new MemoryStream())
    {
        var img = new Bitmap(Image.FromStream(m));
        return img;
    }
    

    希望这会有所帮助。

    【讨论】:

      【解决方案10】:

      我发现如果我保存文件的父文件夹之一有一个尾随空格,那么 GDI+ 会抛出一般异常。

      换句话说,如果我尝试保存到“C:\Documents and Settings\myusername\Local Settings\Temp\ABC DEF M1 Trended Values\Images\picture.png”,那么它会抛出一般异常。

      我的文件夹名称是从一个恰好有一个尾随空格的文件名生成的,因此很容易 .Trim() 并继续前进。

      【讨论】:

      • 太棒了 - 我从来没有想过仔细查看目录路径
      【解决方案11】:

      如果你的代码如下,那么也会出现这个错误

      private Image GetImage(byte[] byteArray)
      {
         using (var stream = new MemoryStream(byteArray))
         {
             return Image.FromStream(stream);
          }
      }
      

      正确的是

      private Image GetImage(byte[] byteArray)
      {
         var stream = new MemoryStream(byteArray))
         return Image.FromStream(stream);        
      }
      

      这可能是因为我们正在从 using 块返回

      【讨论】:

      • 对我来说这是在 using 块中的返回。我仍然使用 using 但我在块之外返回值。谢谢!
      • 我发现“困难的方式”是,如果您再次将该图像保存到新的流中(例如 HttpContext.Response.OutputStream),您还需要执行一个 stream.Flush (),否则错误再次发生。
      【解决方案12】:

      这是 Fred 响应的扩展/限定,其中指出:“GDI 将图像的高度限制为 65534”。我们的一个 .NET 应用程序遇到了这个问题,看到帖子后,我们的外包团队举手表示如果不进行重大更改,他们就无法解决这个问题。

      根据我的测试,可以创建/操作高度大于 65534 的图像,但是当保存到 某些格式 的流或文件时会出现问题。在下面的代码中,当我的像素高度为 65501 时,t.Save() 方法调用会向我们的朋友抛出一般异常。出于好奇,我重复了宽度测试,并且同样的限制适用于保存。

          for (int i = 65498; i <= 100000; i++)
          {
              using (Bitmap t = new Bitmap(800, i))
              using (Graphics gBmp = Graphics.FromImage(t))
              {
                  Color green = Color.FromArgb(0x40, 0, 0xff, 0);
                  using (Brush greenBrush = new SolidBrush(green))
                  {
                      // draw a green rectangle to the bitmap in memory
                      gBmp.FillRectangle(greenBrush, 0, 0, 799, i);
                      if (File.Exists("c:\\temp\\i.jpg"))
                      {
                          File.Delete("c:\\temp\\i.jpg");
                      }
                      t.Save("c:\\temp\\i.jpg", ImageFormat.Jpeg);
                  }
              }
              GC.Collect();
          }
      

      如果您写入内存流,也会发生同样的错误。

      要绕过它,您可以重复上述代码并将 ImageFormat.Tiff 或 ImageFormat.Bmp 替换为 ImageFormat.Jpeg。

      这对我来说最高可达 100,000 的高度/宽度 - 我没有测试限制。碰巧 .Tiff 对我们来说是一个可行的选择。

      请注意

      内存中的 TIFF 流/文件比它们的 JPG 对应物消耗更多的内存。

      【讨论】:

        【解决方案13】:

        由于权限而发生错误。确保文件夹具有所有权限。

        public Image Base64ToImage(string base64String)
            {
                // Convert Base64 String to byte[]
                byte[] imageBytes = Convert.FromBase64String(base64String);
                MemoryStream ms = new MemoryStream(imageBytes, 0,
                  imageBytes.Length);
        
                // Convert byte[] to Image
                ms.Write(imageBytes, 0, imageBytes.Length);
                Image image = Image.FromStream(ms, true);
                return image;
            }
        
         img.Save("YOUR PATH TO SAVE IMAGE")
        

        【讨论】:

        • 我同意你的看法。我用 PERMISSION 解决了​​这个问题
        【解决方案14】:

        已解决 - 我遇到了这个确切的问题。对我来说,解决方法是提高 IIS 服务器上 IUSR 的磁盘配额。在这种情况下,我们有一个目录应用程序,其中包含项目图像等。 “匿名 Web 用户”的上传配额设置为 100MB,这是该特定托管公司的 IIS 服务器的默认值。我将它增加到 400MB,并且能够毫无错误地上传图片。

        这可能不是您的问题,但如果是,它很容易解决。

        【讨论】:

          【解决方案15】:

          在我的情况下,问题出在我保存的路径中(根目录C:\)。将其更改为 D:\111\ 会使异常消失。

          【讨论】:

            【解决方案16】:

            此错误的另一个原因 - 您在 Bitmap 实例的 Save 方法中指定的路径不存在,或者您没有提供完整/有效的路径。

            刚刚出现此错误,因为我传入的是文件名而不是完整路径!

            它发生了!

            【讨论】:

              【解决方案17】:

              轮到我了!

              using (System.Drawing.Image img = Bitmap.FromFile(fileName))
              {
                    ... do some manipulation of img ...
                    img.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);
              }
              

              在 .Save... 上得到它,因为 using() 使文件保持打开状态,所以我无法覆盖它。也许这会对将来的某人有所帮助。

              【讨论】:

                【解决方案18】:

                我遇到了同样的问题。但就我而言,我试图将文件保存在 C 驱动器中,但无法访问。因此,我尝试将其保存在完全可访问的 D 盘中,并且成功了。

                所以首先检查您要保存的文件夹。您必须拥有该特定文件夹的所有(读取和写入)权限。

                【讨论】:

                • 因为通常 c 不允许没有管理员权限。
                【解决方案19】:

                很简单,新建一个Bitmap实例就解决了。

                string imagePath = Path.Combine(Environment.CurrentDirectory, $"Bhatti{i}.png");
                Bitmap bitmap = new Bitmap(image);
                bitmap.Save(imagePath);
                

                【讨论】:

                  【解决方案20】:

                  我注意到您的“jpeg”案例实际上是:

                              default:
                                  format = ImageFormat.Jpeg;
                                  break;
                  

                  您确定格式是 jpeg 而不是其他格式吗?

                  我会尝试:

                              case "image/jpg": // or "image/jpeg" !
                                  format = ImageFormat.Jpeg;
                                  break;
                  

                  或者检查 imageToConvert.MimeType() 实际返回的内容。

                  更新

                  您还需要对 MemoryStream 对象进行任何其他初始化吗?

                  【讨论】:

                  • 谢谢。肯定是以正确的格式调用它。我加载了一个 jpg,调试并确认 mime 被识别为 image/jpeg,格式为 JPG。
                  • 哦,好吧 - 我总是首先尝试消除显而易见的问题。我数不清有多少次我没有这样做,它稍后会回来咬我。
                  【解决方案21】:
                  • 我在测试服务器上遇到了这个问题,但在实时服务器上没有。
                  • 我正在将图像写入流,所以这不是权限问题。
                  • 我一直在直接将一些 .dll 部署到测试服务器。
                  • 部署整个解决方案解决了这个问题,所以这可能是一个奇怪的编译不匹配

                  【讨论】:

                    【解决方案22】:

                    只是为了抛出另一个可能的解决方案,我会提到我遇到此错误消息的情况。方法Bitmap.Save 会在保存我已转换并正在显示的位图时抛出此异常。我发现如果语句上有断点,它不会抛出异常,如果 Bitmap.Save 前面有 Thread.Sleep(500) 也不会抛出异常,所以我想有某种资源争用正在发生。

                    只需将图像复制到新的 Bitmap 对象就足以防止出现此异常:

                    new Bitmap(oldbitmap).Save(filename);
                    

                    【讨论】:

                      【解决方案23】:

                      导致此错误并解决我的问题的另一个原因是您的应用程序对某个目录没有写入权限。

                      所以要完成 savindra 的回答:https://stackoverflow.com/a/7426516/6444829

                      以下是授予 IIS_IUSERS 文件访问权限的方法

                      要提供对 ASP.NET 应用程序的访问权限,您必须授予对 IIs_IUSERS 的访问权限。

                      授予对特定文件或文件夹的读取、写入和修改权限

                      1. 在 Windows 资源管理器中,找到并选择所需的文件。

                      2. 右键单击文件,然后单击属性。

                      3. 在“属性”对话框中,单击“安全”选项卡。

                      4. 在“安全”选项卡上,检查用户列表。 (如果您的应用程序作为网络服务运行,请在列表中添加网络服务帐户并授予其权限。

                      5. 在“属性”对话框中,单击 IIs_IUSERS,然后在“网络服务的权限”部分中,选择“读取”、“写入”和“修改”权限。

                      6. 单击应用,然后单击确定。

                      这在我的 windows server 2016 IIS 和本地 IIS windows 10 中对我有用。

                      【讨论】:

                        【解决方案24】:

                        在生产服务器上使用 ImageProcessor lib 生成 PDF 或调整图像大小时,我们遇到了类似的问题。

                        回收应用程序池修复问题。

                        【讨论】:

                          【解决方案25】:

                          就我而言,路径是错误的

                          就用这个

                          String path = Server.MapPath("~/last_img.png");//Path
                          

                          【讨论】:

                            【解决方案26】:

                            如果您尝试将图像保存到远程位置,请务必将NETWORK_SERVICE 用户帐户添加到安全设置中并授予该用户读取和写入权限。否则它是行不通的。

                            【讨论】:

                              【解决方案27】:
                              byte[] bts = (byte[])page1.EnhMetaFileBits; 
                              using (var ms = new MemoryStream(bts)) 
                              { 
                                  var image = System.Drawing.Image.FromStream(ms); 
                                  System.Drawing.Image img = image.GetThumbnailImage(200, 260, null, IntPtr.Zero);      
                                  img.Save(NewPath, System.Drawing.Imaging.ImageFormat.Png);
                              }
                              

                              【讨论】:

                                【解决方案28】:

                                我也收到此错误,因为我正在尝试使用与以前保存的图像相同的名称保存图像。

                                确保不保存名称重复的图像。

                                例如使用“随机”函数 (How does C#'s random number generator work?) 或者例如生成一个 Guid (http://betterexplained.com/articles/the-quick-guide-to-guids/)

                                【讨论】:

                                  【解决方案29】:

                                  对我来说,我使用的是 Image.Save(Stream, ImageCodecInfo, EncoderParameters),显然这导致了臭名昭著的 A generic error occurred in GDI+ 错误。

                                  我试图使用EncoderParameter 以 100% 的质量保存 jpeg。这在“我的机器”(doh!)上完美运行,而不是在生产中。

                                  当我改用Image.Save(Stream, ImageFormat) 时,错误消失了!所以像个白痴一样,我继续使用后者,尽管它将它们保存为默认质量,我认为它只有 50%。

                                  希望此信息对某人有所帮助。

                                  【讨论】:

                                    【解决方案30】:

                                    我也遇到了这个问题。问题是由于正在处理加载流。但我没有处理它,它在 .Net 框架内。我所要做的就是使用:

                                    image_instance = Image.FromFile(file_name);
                                    

                                    而不是

                                    image_instance.Load(file_name);
                                    

                                    image_instance 是 System.Windows.Forms.PictureBox 类型! PictureBox 的 Load() 处理加载图像的流,我不知道。

                                    【讨论】:

                                      猜你喜欢
                                      • 1970-01-01
                                      • 2011-06-04
                                      • 1970-01-01
                                      • 2013-09-26
                                      • 1970-01-01
                                      • 2015-07-02
                                      • 2014-09-14
                                      • 1970-01-01
                                      相关资源
                                      最近更新 更多