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

时间:2009-06-27 15:40:29

标签: 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();
    }
}

异常的更多细节。造成这么多问题的原因是缺乏解释:(

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. 检索该MIME的编码器,并传递jpeg质量设置。

34 个答案:

答案 0 :(得分:169)

好吧,我似乎只是凭借纯粹的运气找到了原因,而且这个方法没有任何问题,它进一步支持了调用堆栈。

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

// 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),而像Mime类型的Id在所有图像对象上都是正确的因为这样就很难写出通用处理代码。

修改

这不是我最初的搜索,而是here's Jon Skeet的回答

答案 1 :(得分:120)

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

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

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

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

希望对某人有所帮助。

答案 2 :(得分:50)

我将添加错误的原因,希望它能帮助一些未来的网络旅行者。 :)

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

我们做了一些基本的图像调整大小,但在调整大小时我们尝试保持宽高比。我们有一个QA人,他对这份工作有点擅长;他决定使用高度为480像素的一张像素宽照片进行测试。当图像缩放以符合我们的尺寸时,高度为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+."
  }

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

答案 3 :(得分:37)

本文详细解释了究竟发生了什么:Bitmap and Image constructor dependencies

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

所以,而不是

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

试试这个

Stream imageStream;
...

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

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

答案 4 :(得分:28)

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

如果您不确定文件路径是否可用且权限是否正确,请尝试写入文本文件。这只需要几秒钟就可以排除这将是一个非常简单的修复。

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

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

不要忘记清理文件。

答案 5 :(得分:15)

将图像保存到位图变量

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

答案 6 :(得分:13)

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

它应该说,你的路径比#34更糟糕; GDI +中发生了一般错误&#34;

答案 7 :(得分:13)

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

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

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

答案 8 :(得分:12)

保存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;
}

答案 9 :(得分: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();
    }

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

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

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

警告

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

答案 10 :(得分:10)

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

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);        
}

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

答案 11 :(得分:9)

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

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

希望这有帮助。

答案 12 :(得分:5)

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

这可能不是您的问题,但如果是,则可以轻松解决。

答案 13 :(得分:5)

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

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")

答案 14 :(得分:4)

轮到我了!

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

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

答案 15 :(得分:4)

我面临同样的问题。但在我的情况下,我试图在C盘中保存文件,但无法访问。所以我试着将它保存在D驱动器中,这是完全可访问的,我成功了。

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

答案 16 :(得分:4)

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

答案 17 :(得分:4)

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

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

它发生了!

答案 18 :(得分:2)

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

            default:
                format = ImageFormat.Jpeg;
                break;

你确定格式是jpeg而不是别的吗?

我试试:

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

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

<强>更新

您是否需要对MemoryStream对象进行其他初始化?

答案 19 :(得分:2)

  • 我在测试服务器上遇到此问题,但在实时服务器上没有。
  • 我正在将图片写入流中,因此这不是权限问题。
  • 我一直在将部分.dll直接部署到测试服务器上。
  • 部署整个解决方案解决了问题,因此可能是一个奇怪的编译不匹配

答案 20 :(得分:1)

在我的情况下,路径错误

只需使用此

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

答案 21 :(得分:1)

我们在生产服务器上生成PDF时遇到了同样的问题。

回收应用程序池。解决问题。

希望这对某人有帮助。

答案 22 :(得分:1)

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

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

new Bitmap(oldbitmap).Save(filename);

答案 23 :(得分:1)

我也收到此错误,因为我试图保存与之前保存的图片名称相同的图片。

确保您没有保存名称重复的图片。

用于thar例如a&#39; Random&#39;功能(How does C#'s random number generator work?) 或者例如生成Guid(http://betterexplained.com/articles/the-quick-guide-to-guids/

答案 24 :(得分:1)

如果您尝试将图像保存到远程位置,请务必将NETWORK_SERVICE用户帐户添加到安全设置中,并授予该用户读写权限。否则它就无法运作。

答案 25 :(得分:1)

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);
}

答案 26 :(得分:0)

根据@savindra的回答,如果你的应用程序 RHM 并尝试以管理员身份运行,那么它应该可以解决您的问题。

我似乎是一个许可问题。

答案 27 :(得分:0)

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

我试图使用EncoderParameter以100%的质量保存jpeg。这完全适用于我的机器&#34; (doh!)而不是制作。

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

希望此信息可以帮助某人。

答案 28 :(得分:0)

简单,创建一个新的Bitmap实例即可解决该问题。

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

答案 29 :(得分:0)

我的控制台应用程序收到了相同的错误消息:“ GDI +中发生一般错误。” 错误发生在 newImage.Save 行中,请参见以下代码。

$False

当RAM使用量约为4GB时,程序返回错误,并设法通过将项目属性中的 Program Target (程序目标)更改为 x64 来解决该问题。

答案 30 :(得分:0)

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

因此,为了完成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中对我有用。

答案 31 :(得分:0)

对于这个问题,我有一个奇怪的解决方案。 在编码过程中,我对此很着迷。 我认为位图是结构体,并用方法将其环绕。 在我的想象中,位图将在方法内部复制并从方法中返回。 但是后来我检查了它的课程,我不知道为什么它对我有帮助,但是它有效! 也许有人有时间玩得开心,看看这个的IL代码;) 不确定是否可行,因为此方法在static方法中是static方法, 我不知道。

        public SomeClass
        {
            public byte[] _screenShotByte;
            public Bitmap _screenShotByte;
            public Bitmap ScreenShot 
            { 
                get
                {
                    if (_screenShot == null)
                    {
                        _screenShotByte = ScreenShot();
                        using (var ms = new MemoryStream(_screenShotByte))
                        {
                            _screenShot = (Bitmap)Image.FromStream(ms);
                        }
                         
                        ImageUtils.GetBitmap(_screenShot).Save(Path.Combine(AppDomain.CurrentDomain.BaseDirectory) ,$"{DateTime.Now.ToFileTimeUtc()}.png"));
            
                    }
                    return ImageUtils.GetBitmap(_screenShot);
                }
            }
            public byte[] ScreenShot()
            {
                ///....return byte array for image in my case implementedd like selenium screen shot 
            }
        }
        public static ImageUtils
        {
            public static Bitmap GetBitmap(Bitmap image)
            {
                return Bitmap;
            }
        }


p.s。解决这个问题的方法不是在保存其他位置后继续使用位图。

答案 32 :(得分:0)

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

image_instance = Image.FromFile(file_name);

而不是

image_instance.Load(file_name);

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

答案 33 :(得分:-1)

可能导致此类错误的问题是:

  1. 目录不存在(正在调用的方法不会自动为您创建此目录)
  2. 写入输出目录的安全权限不允许运行应用程序的用户写入

我希望这会有所帮助,这是解决我的问题的方法,我只是在保存输出图像之前确保输出目录存在!