记录一次踩坑:webp与ios
最近有同事遇到一个问题,有个页面的图片(www.xxx.jpg)在安卓和 pc 都能正常显示,但是用 ios 就展示不了。通常出现在这种情况是因为 WKWebview 不支持 webp 的图片导致的。通过 https://caniuse.com/?search=webp,可以看到 safari 在 14.0 之后才支持 webp。
虽然这个场景里的图片后缀名是 jpg,但文件的后缀名并不能准确反映文件的实际格式。因此在 pc 端打开链接后,查看 Content-Type 看到属性为 image/webp, 因此可以确定这个图片文件实际上是 webp。
这个图片是在后台系统上传的,那么有可能是操作人员从网络下载了一张 webp 的图片,之后修改后缀名并上传。
上传组件是通过 antdesign 提供的Upload(该组件本质上也是封装了 input 组件)。 在 beforeUpload 可以获取到上传的 file, 上传前通过 file.type 校验过文件的 MIME type 必须是 image/jpeg。
奇怪的这里 webp 文件,如果后缀名改成 jpg, 则 file.type 显示的是 image/jpeg 而非 image/webp, 即 file.type 获取到类型会收到后缀名的影响,并不是准确的文件类型。
根据 MDN 的描述
Based on the current implementation, browsers won’t actually read the bytestream of a file to determine its media type. It is assumed based on the file extension; a PNG image file renamed to .txt would give “text/plain“ and not “image/png“. Moreover,
file.typeis generally reliable only for common file types like images, HTML documents, audio and video. Uncommon file extensions would return an empty string. Client configuration (for instance, the Windows Registry) may result in unexpected values even for common types. Developers are advised not to rely on this property as a sole validation scheme.
确实浏览器会依据后缀名来判断 File.type 而非真正去读取文件流内容。
那么如何判断文件的类型是否为我们需要的类型呢?有同事给了段代码:
var reader = new FileReader();
reader.onload = function() {
const isJPG = z => [255,216,255].every((v, k) => z.charCodeAt(k)===v);
const isWEBP = z => [87,69,66,80].every((v, k) => z.charCodeAt(k+8)===v);
const isPNG = z => [137,80,78,71,13,10,26,10].every((v, k) => z.charCodeAt(k) === v);
const isGIF = z => [71,73,70].every((v, k) => z.charCodeAt(k) === v);
var res = reader.result;
console.log(isJPG(res));
console.log(isWEBP(res));
console.log(isPNG(res));
console.log(isGIF(res));
};
reader.readAsBinaryString($0.files[0]);