You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

iOS11下如何在上传前用JavaScript检测HEIC图片?

这个iOS11的HEIC转JPG坑确实让人头疼——系统偷偷改了文件的扩展名和MIME类型,导致常规检测完全失效,还触发compress.js崩溃。我来给你个靠谱的解决方案,从文件的二进制签名入手检测,绕过iOS的伪装:

问题根源

iOS11在用户选择HEIC/HEIF图片后,会自动将文件重命名为.jpg,并把MIME类型设为image/jpg,但这个转换后的文件其实是不兼容compress.js的“伪JPG”,导致压缩时崩溃。常规的MIME检测、accept属性限制都因为iOS的静默修改而失效。

解决方案:检测文件二进制签名

不管iOS怎么改扩展名和MIME类型,HEIC文件的二进制文件头签名是不会变的。我们可以读取文件的前几个字节,对比HEIC的专属签名来识别它。

1. 定义HEIC的文件签名

HEIC/HEIF文件的开头8字节通常是以下几种签名之一:

const HEIC_SIGNATURES = [
  [0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x63], // ftypheic
  [0x66, 0x74, 0x79, 0x70, 0x68, 0x65, 0x69, 0x78], // ftypheix
  [0x66, 0x74, 0x79, 0x70, 0x6D, 0x69, 0x66, 0x31]  // ftypmif1(HEIF变种)
];

2. 编写检测函数

FileReader读取文件的前8字节,对比签名:

function isHEICFile(file) {
  return new Promise((resolve) => {
    const reader = new FileReader();
    // 只读取前8字节,性能高效
    reader.readAsArrayBuffer(file.slice(0, 8));
    
    reader.onload = (e) => {
      const uint8Array = new Uint8Array(e.target.result);
      let isHEIC = false;
      
      // 逐一对比签名
      HEIC_SIGNATURES.forEach(signature => {
        let match = true;
        for (let i = 0; i < signature.length; i++) {
          if (uint8Array[i] !== signature[i]) {
            match = false;
            break;
          }
        }
        if (match) isHEIC = true;
      });
      
      resolve(isHEIC);
    };
    
    // 读取失败时默认返回非HEIC
    reader.onerror = () => resolve(false);
  });
}

3. 在文件选择事件中使用检测

修改你的原有代码,先检测HEIC,再处理常规图片:

$(document).on('change', '[id=uploading_file]', async (evt) => {
  'use strict';
  const files = [...evt.target.files];
  const uploadingFile = files[0];
  
  // 优先检测HEIC(即使被iOS伪装成jpg)
  const isHEIC = await isHEICFile(uploadingFile);
  if (isHEIC) {
    alert('HEIC格式图片暂不支持上传,请选择JPG/PNG等格式的图片');
    evt.target.value = ''; // 清空文件选择,避免重复提交
    return;
  }
  
  // 常规图片格式验证
  const ValidImageTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/bmp"];
  if (ValidImageTypes.includes(uploadingFile.type)) {
    // 这里执行你的compress.js压缩和上传逻辑
    console.log('开始处理有效图片');
  } else {
    alert('请选择支持的图片格式');
    evt.target.value = '';
  }
});

额外说明

  • accept属性在iOS文件选择器中不可靠,系统经常会忽略该限制,所以必须配合二进制检测。
  • 只读取文件的前8字节,不会影响性能,也不会读取整个大文件。

内容的提问来源于stack exchange,提问作者S.Basnagoda

火山引擎 最新活动