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

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类型。

修改后的代码思路:

  1. 读取文件前1024字节(足够覆盖WhatsApp可能嵌入的缩略图长度)
  2. 在字节中查找MP4的ftyp标识,定位真正的视频起始位置
  3. 用该位置的字节判断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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 19:32:22