GDI +,JPEG Image to MemoryStream中发生一般错误

mad*_*kay 314 c# gdi+

这似乎是整个网络上一个臭名昭着的错误.因为我的情景不适合,所以我无法找到问题的答案.将图像保存到流时会抛出异常.

奇怪的是这与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();
    }
}
Run Code Online (Sandbox Code Playgroud)

更多细节到例外.造成这么多问题的原因是缺乏解释:(

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: 
Run Code Online (Sandbox Code Playgroud)

好的,到目前为止我已经尝试过了.

  1. 克隆图像并进行处理.
  2. 检索该MIME的编码器,并使用jpeg质量设置传递该编码器.

mad*_*kay 178

好吧,我似乎只是通过纯粹的运气找到了原因,并且这个特定方法没有任何问题,它进一步支持调用堆栈.

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

// 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;
 }
Run Code Online (Sandbox Code Playgroud)

看来,该对象被创建的内存流已经是在保存对象时打开.我不知道为什么会这样.是否有人能够启发我以及如何解决这个问题.

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

编辑

这在我最初的搜索中没有出现,但这 Jon Skeet的回答

  • 谢谢.这可能挽救了我的最后一根头发. (37认同)
  • 谢谢!这节省了我很多时间,但有一件事,你会介意在你的答案开头突出错误的原因,因为我(我猜大多数傻瓜)错过了原始的撇去答案,可能是类似"如果您打算再次使用图像,请不要关闭记忆流"会很棒; D (5认同)
  • 你的"dst"变量是什么? (5认同)
  • 我没有意识到当你从内存流中获取位图时,你不应该关闭流.非常有帮助,谢谢 (3认同)
  • 这一切都归结为原生 WinAPI 以及 Microsoft 不稳定的开发方式。如果您感兴趣,在 .NET 内部,流是使用 [`CreateStreamOnHGlobal`](https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-createstreamonhglobal) 函数分配的将“fDeleteOnRelease”设置为“TRUE”,这会导致流与“Image”对象一起被释放/释放。这是一个完全“原生”的问题,所以我不确定为什么你们必须在托管代码中处理它...... (2认同)

小智 125

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

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

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

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

希望它有所帮助.

  • 你没有!我浪费了2个小时的该死的写权限......来到这里发布这个.希望你得到更多的赞成.:) (6认同)
  • 在保存位图之前,您可以执行File.WriteAllText("filename.jpg",""),然后执行File.DeleteFile("filename.jpg").在我的benmark这只需要.001秒,你得到一个很好的'你没有权限保存filename.jpg那里' (5认同)
  • 这是我的解决方案.完全+1! (2认同)
  • 就我而言,该目录不存在。 (2认同)

Fre*_*red 52

我将添加这个错误的原因,希望它有助于一些未来的互联网旅行者.:)

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

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

您可以使用test自行验证:

  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+."
  }
Run Code Online (Sandbox Code Playgroud)

太糟糕了ArgumentException,在构造函数中没有抛出友好的.net Bitmap.

  • 谢谢 - 这位网络时间旅行者非常感谢你留下这条消息. (17认同)

Iva*_*sic 37

本文详细解释了究竟发生了什么:Bitmap和Image构造函数依赖项

简而言之,对于Image构造的生命周期,不得破坏流.

所以,而不是

using (var strm = new ... )  {
    myImage = Image.FromStream(strm);
}
Run Code Online (Sandbox Code Playgroud)

试试这个

Stream imageStream;
...

    imageStream = new ...;
    myImage = Image.FromStream(strm);
Run Code Online (Sandbox Code Playgroud)

并关闭窗体关闭或网页关闭imageStream.


Kir*_*rst 28

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

如果您不是100%确定文件路径可用且权限正确,那么尝试写入文本文件.这需要几秒钟来排除这将是一个非常简单的修复.

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

// img.Save(path);
System.IO.File.WriteAllText(path, "Testing valid path & permissions.");
Run Code Online (Sandbox Code Playgroud)

不要忘记清理文件.


小智 17

将图像保存到位图变量

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


Axi*_*ili 16

保存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
  }
Run Code Online (Sandbox Code Playgroud)

我没有创建图像所以我不知道有什么区别.
如果有人能解释,我会很感激.

这是我的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;
}
Run Code Online (Sandbox Code Playgroud)


ahs*_*ant 16

以防万一有人像我一样做蠢事.1.确保路径确实存在.2.确保你有权写.3.确保你的路径是正确的,在我的情况下,我在TargetPath中丢失了文件名:(

应该说,你的路径比"GDI +中发生的一般性错误"糟透了


小智 13

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

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

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

  • 真棒 - 我永远不会想到密切关注目录路径 (3认同)

vip*_*pes 11

这是弗雷德回应的扩展/限定,其中说:"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();
    }
Run Code Online (Sandbox Code Playgroud)

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

为了解决这个问题,您可以重复上述代码,并将ImageFormat.Tiff或ImageFormat.Bmp替换为ImageFormat.Jpeg.

这对我来说高达10万的高度/宽度 - 我没有测试极限.碰巧.Tiff对我们来说是一个可行的选择.

被警告

内存中的TIFF流/文件比JPG对应的内存消耗更多内存.


dhi*_*esh 10

如果您的代码如下,那么也会发生此错误

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

正确的是

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

这可能是因为我们从使用块返回


小智 9

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

using (var m = new MemoryStream())
{
    var img = new Bitmap(Image.FromStream(m));
    return img;
}
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.


viv*_*shi 6

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

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")
Run Code Online (Sandbox Code Playgroud)


小智 5

已解决 - 我遇到了这个问题.对我来说,修复是在IIS服务器上增加IUSR的磁盘配额.在这种情况下,我们有一个目录应用程序,其中包含项目等的图像."匿名Web用户"的上载配额设置为100MB,这是此特定主机公司的IIS服务器的默认值.我把它增加到400MB,并能够无误地上传图像.

这可能不是你的问题,但如果是,那就很容易解决.


Ani*_*Ani 5

就我而言,问题出在我保存的路径( root C:\)中。更改它以D:\111\使异常消失。


Has*_*man 5

很简单,创建一个新的 Bitmap 实例就可以解决问题。

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