当前位置: 代码迷 >> 综合 >> 「C#」Bitmap/Image.Save()报错“GDI+ 中发生一般性错误”的一个案例总结
  详细解决方案

「C#」Bitmap/Image.Save()报错“GDI+ 中发生一般性错误”的一个案例总结

热度:55   发布时间:2023-12-07 05:27:46.0

先说一下遇到这个错误的地方

static void Main(string[] args)
{string imgPath = "C:\\Users\\raink\\Desktop\\微信图片_20210724102738.jpg";Image bmp = GetImageByFileName(imgPath);//编码参数EncoderParameters encoderParameters = new EncoderParameters(1);//设置质量EncoderParameter encoderParameter = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 70L);encoderParameters.Param[0] = encoderParameter;var stream = new MemoryStream();//该行报错!!!!????????????????bmp.Save(stream, GetImageCodecInfo(ImageFormat.Jpeg), encoderParameters);//以下不重要,就是按文件流保存一下using (var fileStream = File.Create(Path.Combine("C:\\Users\\raink\\Desktop\\", "pp.jpg"))){stream.Seek(0, SeekOrigin.Begin);stream.CopyTo(fileStream);fileStream.Flush(true);}
}//比较坑的一个方法
public static Image GetImageByFileName(string fileName)
{if (string.IsNullOrEmpty(fileName)) return null;FileStream s = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);            var img = Image.FromStream(s);            s.Close();s.Dispose();return img;
}
private static ImageCodecInfo GetImageCodecInfo(ImageFormat imageFormat)
{ImageCodecInfo[] imageCodecInfoArr = ImageCodecInfo.GetImageDecoders();foreach (ImageCodecInfo imageCodecInfo in imageCodecInfoArr){if (imageCodecInfo.FormatID == imageFormat.Guid){return imageCodecInfo;}}return null;
}

简单的说:

使用filestream读取文件到流s

使用Image.Fromstrea(s)到图片,然后关闭并dispose流s

那前面的图片取做解码压缩,

然后再使用Image.Save()方法保存到一个MemoryStream中,

这个保存就会保存,而且只针对JPG图像。

这个代码一开始是正常的,直到遇到某一张jpg出错后,所有的jpg都出错,以前能正常运行的jpg也变得不行了,重启电脑也没用。

真的是及其神奇!!!简直WC。。。

-------------------------------------

网上找了很多资料,大家同意说的原因如下

1.保存路径不存在或者错误;

2.权限问题

3.“Bitmap 对象或从一个文件构造一个 图像对象时,该文件仍保留锁定对于对象的生存期。 因此, 无法更改图像并将其保存回它产生相同的文件”

我觉得与我遇到的情况都不符合。

我尝试过不成功的方法很多:filestream创建时各种权限设置、文件换路径、换名字……

------------------------------------------------

现有的解决办法如下面的代码,Main方法中看注释

static void Main(string[] args)
{string imgPath = "C:\\Users\\raink\\Desktop\\微信图片_20210724102738.jpg";Image bmp = GetImageByFileName(imgPath);//解决方法1//Image bmp = GetImageByFileNameV2(imgPath);  // FileStream通过byte[]转换MemoryStream再转换成img//解决方法2//使用原有filstream方案,读图后复制图//Bitmap img = CopyImgByBytes(bmp)  //或者使用CopyImgByDraw(bmp)//bmp.dispose   //后面全部用img//解决方法3://对filestream读上来的图像先加锁再解锁//Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);//System.Drawing.Imaging.BitmapData bmpData =//    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,//    bmp.PixelFormat);//bmp.UnlockBits(bmpData);//编码参数EncoderParameters encoderParameters = new EncoderParameters(1);//设置质量EncoderParameter encoderParameter = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 70L);encoderParameters.Param[0] = encoderParameter;var stream = new MemoryStream();//该行报错!!!!?????????????????System.Runtime.InteropServices.ExternalException:“GDI+ 中发生一般性错误。”bmp.Save(stream, GetImageCodecInfo(ImageFormat.Jpeg), encoderParameters);//解决方法4://基于原始图像创建新的bitmap对象(类似于方法2)//new Bitmap(bmp).Save(stream, GetImageCodecInfo(ImageFormat.Jpeg), encoderParameters);//以下不重要using (var fileStream = File.Create(Path.Combine("C:\\Users\\raink\\Desktop\\", "pp.jpg"))){stream.Seek(0, SeekOrigin.Begin);stream.CopyTo(fileStream);fileStream.Flush(true);}
}
public static Image GetImageByFileName(string fileName)
{if (string.IsNullOrEmpty(fileName)) return null;FileStream s = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);//return Image.FromFile(fileName);var img = Image.FromStream(s);s.Close();s.Dispose();return img;
}
private static ImageCodecInfo GetImageCodecInfo(ImageFormat imageFormat)
{ImageCodecInfo[] imageCodecInfoArr = ImageCodecInfo.GetImageDecoders();foreach (ImageCodecInfo imageCodecInfo in imageCodecInfoArr){if (imageCodecInfo.FormatID == imageFormat.Guid){return imageCodecInfo;}}return null;
}
/// <summary>
/// FileStream通过byte[]转换MemoryStream再转换成img
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
private static Image GetImageByFileNameV2(string fileName)
{FileStream s = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);BinaryReader r = new BinaryReader(s);r.BaseStream.Seek(0, SeekOrigin.Begin);    //将文件指针设置到文件开byte[] bytes = r.ReadBytes((int)r.BaseStream.Length);r.Close();r.Dispose();s.Close();s.Dispose();MemoryStream memoryStream = new MemoryStream(bytes);Image img = Image.FromStream(memoryStream);memoryStream.Close();memoryStream.Dispose();return img;
}
//图像拷贝
public static Bitmap CopyImgByBytes(Bitmap bmp)
{Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);Bitmap img = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat);BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);BitmapData imgData = img.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);IntPtr optr = bmpData.Scan0;IntPtr nptr = imgData.Scan0;int bytes = Math.Abs(bmpData.Stride) * bmp.Height;byte[] OriginalImgBytes = new byte[bytes];//把原始图像数据复制到byte[]数组中Marshal.Copy(optr, OriginalImgBytes, 0, bytes);//把原始图像byte[]数据复制到新图像中Marshal.Copy(OriginalImgBytes, 0, nptr, bytes);//解除锁定bmp.UnlockBits(bmpData);img.UnlockBits(imgData);return img;
}
public static Bitmap CopyImgByDraw(Bitmap bmp)
{Bitmap nbmp = new Bitmap(bmp.Width, bmp.Height, bmp.PixelFormat);Graphics graphics = Graphics.FromImage(nbmp);graphics.DrawImage(bmp, 0, 0);graphics.Dispose();return nbmp;
}

做一个大概说明:

1、FileStream先转换到MemoryStream,

没有直接转换的方法,中间通过byte[]做中介。

2、按原来的方法,通过filestream拿到image,先把image深拷贝到另一个对象中,然后dispose掉前面从fs中拿到的image,后续使用新复制的这个对象就好

3、按原来的方法,通过filestream拿到image,然后对他的BitmapData数据去先加锁再解锁就好。

4、类似于方法2,基于从fs中得到的image,new一个新的bitmap,去执行后面的操作。

---------------------------------

还有2个方法能解决报错

1、读取图片使用Image.FromFile(),但是这个方法会一直占用文件资源,因为业务逻辑的原因,也不能很快释放,导致其他部位访问时会出现文件被占用的情况。

2、就是在filestream读取完图片后,不执行s.close()和s.dispose(),但是这样和上面1 的原因就一样了,文件会被占用。

所以这两个方法没有采用

  相关解决方案