You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.27 15:36:04