C# Image.FromFile处理JPEG抛OutOfMemoryException致缩略图生成失败
Image.FromFile 生成缩略图时抛出 OutOfMemoryException 排查与修复 注意:GDI+ 原生实现的
Image.FromFile抛出该异常时,绝大多数场景和系统物理内存不足没有关系,是方法内部对错误场景的统一异常映射,不要盲目调整内存配置。
核心触发原因
- 文件内容与后缀名不匹配:从外部URL下载的文件经常出现“存为.jpg后缀但实际是WebP、PNG、HTML错误页、下载残片”的情况,GDI+ 不支持解码这类内容时,会统一抛出
OutOfMemoryException,而非格式不匹配的对应异常。 - JPEG 编码不兼容:部分 JPEG 采用 CMYK 色彩空间、非标准EXIF扩展、特殊参数的渐进式编码时,GDI+ 的内置解码器存在兼容缺陷,读取时会触发该异常。
- 文件权限不足:当前进程没有对应源JPEG文件的读取权限时,GDI+ 也会将读取失败映射为该异常。
- 代码逻辑缺陷叠加:
- 现有宽高比计算使用整数除法,比如
(srcHeight / srcWidth) * thumbWidth,遇到非整数比例的原图时极易算出尺寸为0的位图,触发解码异常。 - 现有代码没有全流程的资源释放逻辑:任意步骤抛出异常时,
Image、Bitmap、Graphics持有的GDI非托管句柄无法被释放,反复多次报错后会出现GDI句柄耗尽,进一步放大异常出现的概率。
- 现有宽高比计算使用整数除法,比如
修复实现
- 读取文件前先校验JPEG标准文件头(
FF D8 FF),非合规文件直接跳过,不进入解码流程。 - 宽高比计算替换为浮点数运算后取整,给计算出的缩略图尺寸加最小1x1的限制,避免生成0尺寸位图。
- 用
using语句包裹所有GDI非托管对象,无论流程是否抛出异常都自动释放资源,避免句柄泄漏。
修正后的代码示例:
try { // 先校验JPEG文件头,避免解码非合规文件 var fileBytes = File.ReadAllBytes(image_Path); if (fileBytes.Length < 3 || fileBytes[0] != 0xFF || fileBytes[1] != 0xD8 || fileBytes[2] != 0xFF) { return; } using (Image image = Image.FromFile(image_Path)) { int srcWidth = image.Width; int srcHeight = image.Height; const int thumbBaseSize = 72; int thumbWidth, thumbHeight; // 用浮点数计算宽高比,避免整数除法计算偏差 if (srcHeight > srcWidth) { thumbWidth = thumbBaseSize; thumbHeight = Math.Max(1, (int)Math.Round((double)srcHeight / srcWidth * thumbBaseSize)); } else { thumbHeight = thumbBaseSize; thumbWidth = Math.Max(1, (int)Math.Round((double)srcWidth / srcHeight * thumbBaseSize)); } using (Bitmap bmp = new Bitmap(thumbWidth, thumbHeight)) using (Graphics gr = Graphics.FromImage(bmp)) { gr.SmoothingMode = SmoothingMode.HighQuality; gr.CompositingQuality = CompositingQuality.HighQuality; gr.InterpolationMode = InterpolationMode.High; Rectangle destRect = new Rectangle(0, 0, thumbWidth, thumbHeight); gr.DrawImage(image, destRect, 0, 0, srcWidth, srcHeight, GraphicsUnit.Pixel); bmp.Save(destinationfile); } } } catch (OutOfMemoryException ex) { // 记录触发异常的具体文件路径,定位兼容问题 Console.WriteLine($"处理失败,文件路径: {image_Path}, 异常信息: {ex.Message}"); }
补充说明
如果排查后确认触发异常的文件是CMYK色彩空间的JPEG,这类文件GDI+原生不支持解码,需要替换为支持CMYK解码的图像处理组件实现,不能靠调整现有GDI+代码解决。
内容的提问来源于stack exchange,提问作者TrinTrin
相关产品推荐
相关产品推荐

