img.decode()报错的图能画,load成功的却是空的

我的上传组件里有一段判断图片能不能用的代码。它先等 onload 再 await img.decode(),任何一步失败就提示「图片损坏,请换一张」。这次我想看大图会卡在哪一步。我拿脚本造了一批合成大图,边长从 3000 px 到 25000 px。它们全是程序生成的渐变、色块和噪声,不是照片。我跑了三个内核:Chromium 149 的开源构建(不是正式版 Chrome)、WebKit 26.5 构建(不等于 Safari)和 Firefox 151 构建。结果这段判断在两个方向上都错了。

开始之前我先确认过 1 亿像素这一档在浏览器里本身没问题。我用的是图映 ImgIng(imging.cn/)的压缩转换做对照。1... 的合成 JPEG 转 WebP 和 JPG 在三个内核都处理完了。12 个用例里我抓到的非 GET 请求是 0 次。图既然解得开,问题就出在我自己那段判断上。

img.decode() 报错,图片一定坏了吗?

不一定。Chromium 149 开源构建在边长 6000 px 及以上的 JPEG 上,img.decode() 6 次里 6 次都以 EncodingError reject。后来复跑时门槛挪到了 6000 和 8000 之间。5000 px 那档是 6 次里 3 次。4000 px 以下一次都没有。同一张图的 onload 却照常触发,画进 64×64 的小画布以后也没有一个透明像素。我拿 1 亿像素那张的缩略图和生成时记下的分块均值比过,平均误差 1.88。它就是这张图本身。按我原来的写法,6000 px 以上的图在 Chromium 上全会被判成损坏。

5000 这档一半一半最让我头疼。我用三个新浏览器实例和两种调用顺序各跑了一遍。结果并不只取决于调用顺序,原因我没查到。这个现象我只在这个开源构建上见过,正式版 Chrome 没验证。WebKit 和 Firefox 构建在 1000 到 10000 这 10 档上 decode() 全部成功。

onload 成功了,画出来为什么是空的?

反方向的坑出在 Firefox 151 构建。23170×23170 和 25000×25000 的 JPEG 它照样触发 load,70000×1500 的 PNG 也一样。naturalWidth 给出的还是真实宽度。画出来却是全透明的。这时唯一能抓到的信号恰恰是 img.decode() 的 reject。控制台只对 PNG 那一例报了「Image corrupt or truncated.」而两张 JPEG 没有。

这是我写的复现小页面,在 Firefox 151 构建里选了一张 25000×25000 的图。左边的预览是 <img> 本身,图好好地在那儿。下面也写着 25000 × 25000。中间红框里同一张图画进 380×380 画布后只剩棋盘格。右边原尺寸导出直接是 Export failed。用户看到的就是处理完一片白。

Chromium 149 在这附近有自己的边界。23170×23170 的 <img> 能解,23171×23171 直接 onerror。两者之间正好跨过 2^29 像素。同一个 decode() 报错,在我测的这批 Chromium 样本里是误报。换到 Firefox 构建里它却可能意味着图真的空了。只看任何一个信号都会判错。

怎么判断大图真的解出来了?

我最后不再信任事件和 Promise,改成看像素。等 onload 之后先把图画进一张 64×64 的小画布。再读回来看是不是全透明。全透明就当解不了。接下来走缩图或者提示用户。decode() 只留在日志里而不再拿来拦截。这张小画布只占 16 KB,比原尺寸画布便宜得多。

这个办法还有一块我没验证完。WebKit 构建的 createImageBitmap(blob) 在 23170 px 见方以上会返回尺寸正确但全透明的位图。我在 WebKit 上就没敢用它换掉 <img>。

左右两格是同一张 23170×23170 的 JPEG。左边走 <img> 的缩略图是对的。右边走 createImageBitmap 的那格在红框里一个像素都没有。你的组件如果在 decode() 失败时提示「图片损坏」,先拿一张 8000 px 见方的合成 JPEG 在 Chromium 里试一次。再把读回像素那一步加上。

相关推荐
不可能片场41 分钟前
AI视频首尾帧控制 用参考图定住镜头
前端·electron
修炼的dance1 小时前
canvas最大能开多大:先看面积,再看导出格式
前端
薛一半1 小时前
React-Redux核心原理与容器组件实战
前端·javascript·react.js
做运维的阿瑞1 小时前
自连接、交叉连接与复杂 JOIN:一条问题链讲透
前端·数据库·mysql
言乐61 小时前
JavaScript概括前端原理
开发语言·前端·javascript·python·ecmascript
回眸&啤酒鸭2 小时前
DeepSeek V4.1 Flash 批量处理实战指南
服务器·前端·javascript
用户3374096924262 小时前
2026-09-27-Qwen-Image-2.1-1660Ti本地部署实战
前端
BIM云平台开发2 小时前
DAZ里,如何保存现在姿势,如果调错了,可以重新导回
linux·服务器·前端·daz基础教程
传人2 小时前
网页文字滚动效果如何做
前端·javascript·jquery