Flutter应用中WhatsApp视频被误识别为图片的问题排查
问题分析与解决
核心原因
你遇到的问题大概率是WhatsApp在iOS上保存视频时,会在MP4文件头部嵌入JPEG格式的缩略图,导致你读取的前12字节是JPEG的文件头(0xFFD8FF),而非MP4的ftyp标识。这就导致lookupMimeType不管传不传头字节,都会误识别成图片类型。
另外你当前的代码逻辑是「返回true代表是图片,false代表是视频」,一旦lookupMimeType因为错误的头字节返回image/jpeg,自然会把视频误判成图片。
读取文件头的方式问题
你用file.openRead(0, 12).first读取前12字节的方式本身没问题,但架不住WhatsApp给文件塞了额外的前置数据。MP4的标准文件头是从ftyp(十六进制0x66747970)开始的,而JPEG的头是0xFFD8FF,如果文件开头是JPEG缩略图数据,读取前12字节肯定会匹配到图片类型。
解决方案
既然文件里混了JPEG缩略图和MP4内容,我们需要跳过开头的JPEG数据,找到MP4的真正起始位置,再判断MIME类型。
修改后的代码思路:
- 读取文件前1024字节(足够覆盖WhatsApp可能嵌入的缩略图长度)
- 在字节中查找MP4的
ftyp标识,定位真正的视频起始位置 - 用该位置的字节判断MIME类型,没找到则 fallback 到原逻辑
以下是修正后的代码:
// true if image, false if video Future<bool> isImage(File file) async { // 读取前1024字节,覆盖可能的前置缩略图 final List<int> fileBytes = await file.openRead(0, 1024).first; // 查找MP4的ftyp标识(十六进制0x66747970,对应ASCII的'ftyp') int mp4StartIndex = -1; for (int i = 0; i <= fileBytes.length - 4; i++) { if (fileBytes[i] == 0x66 && fileBytes[i+1] == 0x74 && fileBytes[i+2] == 0x79 && fileBytes[i+3] == 0x70) { mp4StartIndex = i; break; } } String? mimeType; if (mp4StartIndex != -1) { // 从MP4起始位置取12字节判断类型 final List<int> mp4Header = fileBytes.sublist(mp4StartIndex, mp4StartIndex + 12); mimeType = lookupMimeType(file.path, headerBytes: mp4Header); } else { // 没找到MP4标识,用原逻辑判断 mimeType = lookupMimeType(file.path, headerBytes: fileBytes); } // 优先判断是否为视频,再判断是否为图片 if (mimeType?.startsWith('video/') == true) { return false; } return mimeType?.startsWith('image/') == true; }
额外说明
- 为什么不依赖后缀?因为iOS上文件后缀可被随意修改,WhatsApp就是典型案例,必须以文件实际内容为准。
- 读取前1024字节是经验值,若遇到更长的前置缩略图,可适当调整这个数值。
内容的提问来源于stack exchange,提问作者Yoann Faraus
相关产品推荐
相关产品推荐

