先说一下遇到这个错误的地方
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;
}
///
/// FileStream通过byte[]转换MemoryStream再转换成img
///
///
///
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 的原因就一样了,文件会被占用。
所以这两个方法没有采用