×

exif js 是啥?能解决哪些实际问题?

作者:Terry2025.11.18来源:Web前端之家浏览:33评论:0

exif js 是啥?能解决哪些实际问题?

不少做前端开发的同学,碰到需要读取照片拍摄参数、处理图片方向这些需求时,总会疑惑“js 里的 exif - js 是干啥的?咋用起来?碰到问题咋解决?”今天就从是什么、怎么用、实战技巧和避坑这几块,把 exif - js 掰碎了讲明白,不管是做图片预览、摄影社区还是电商商品图处理,都能搞懂咋用~

先搞清楚「EXIF」是啥——它是“可交换图像文件格式”里的元数据,简单说就是照片里藏着的“隐形信息”,比如你用手机/相机拍的照片,里面可能存了拍摄设备型号(像“iPhone 15 Pro”)、拍摄时间、GPS经纬度、照片旋转方向这些数据,但浏览器默认不会把这些信息暴露给前端 JS,这时候就得靠 exif - js 这个库帮忙,让前端能读取、处理这些元数据。

那实际开发中能解决啥问题?举几个常见场景:

  • 摄影社区/博客:让用户上传照片后,自动展示“拍摄设备、光圈、快门、拍摄时间”这些参数,不用用户手动填;

  • 电商平台:商品详情图如果是摄影师实拍,用 exif 记录拍摄设备和时间,防止竞品盗用后伪造“原创”;

  • 图片上传预览:手机拍照后直接上传,照片在电脑上显示是“歪的”(因为手机拍照时 Orientation 信息没处理),exif - js 能读方向信息,配合 canvas 自动把照片转正;

  • 社交 APP:读取照片 GPS 信息,在地图上标记“这张照片在哪拍的”,增强互动感。

从零开始用 exif - js ,步骤是啥?

想用 exif - js ,得先把库引入项目,再跟着“选文件→读二进制→解析元数据”的流程走,下面一步步拆解:

安装/引入 exif - js

有两种方式:

  • 用 npm 安装:在项目里执行 npm install exif - js,然后在代码里引入 import EXIF from 'exif - js';

  • 用 CDN 直接引入:在 HTML 里加

    (版本号可换最新的)。

基本使用流程(以“读取单张照片的拍摄设备”为例)

核心逻辑是:用户选文件 → JS 把文件转成二进制数据 → 用 exif - js 解析数据 → 拿到想要的元数据,看代码更直观:

<!-- 页面里放个文件选择框 -->
<input type="file" id="fileInput" accept="image/*" />
<script>
  const fileInput = document.getElementById('fileInput');
  fileInput.addEventListener('change', function(e) {
    // 1. 拿到用户选的文件
    const file = e.target.files[0];
    if (!file) return; // 没选文件就退出
    // 2. 用 FileReader 把文件读成 ArrayBuffer(二进制数据)
    const reader = new FileReader();
    reader.readAsArrayBuffer(file);
    // 3. 读取完成后,用 exif - js 解析
    reader.onloadend = function() {
      const buffer = reader.result; // 拿到二进制数据
      const tags = EXIF.readFromBinaryFile(buffer); // 解析出所有元数据
      // 4. 取想要的信息,比如相机型号(Model)
      const cameraModel = tags.Model;
      console.log('这张照片是用啥拍的?', cameraModel);
      // 还能读其他信息,比如拍摄时间(DateTimeOriginal)、方向(Orientation)
      const shootTime = tags.DateTimeOriginal;
      const orientation = tags.Orientation;
      console.log('拍摄时间:', shootTime, '照片方向:', orientation);
    };
  });
</script>

这里要注意几个关键 API:

  • EXIF.readFromBinaryFile(ArrayBuffer):把文件的二进制数据丢进去,返回一个包含所有 EXIF 信息的对象(叫 tags);

  • 如果只想读单个字段,用 EXIF.getTag(tags, 'TagName')EXIF.getTag(tags, 'Model'));

  • 想一次性拿到所有信息,用 EXIF.getAllTags(tags)(不过和直接用 tags 区别不大)。

常见元数据解析要点

不同场景要关注不同字段,这里列几个高频需求:

  • 照片方向(Orientation):值是 1 - 8 的数字,代表照片旋转/翻转的方向(6 代表“顺时针转 90 度”),手机拍照后上传,前端预览时必须处理这个值,否则照片会“歪着显示”;

  • GPS 信息(GPSLatitude、GPSLongitude):存的是“度分秒”格式(比如纬度可能是 [30, 12, 34.56]),还要结合 GPSLatitudeRef(N 北纬/S 南纬)、GPSLongitudeRef(E 东经/W 西经)转成十进制经纬度;

  • 拍摄设备(Model)拍摄时间(DateTimeOriginal):这俩属于“一眼能看懂”的信息,直接读就行。

实战:这些场景用 exif - js 能玩出花!

光看 API 不够,得结合真实需求落地,下面挑三个典型场景,讲讲怎么用 exif - js 解决问题~

场景1:手机拍照上传,自动校正预览方向

手机拍照时,系统会把“照片该咋旋转”的信息存在 Orientation 里,但浏览器直接显示照片时,不会自动处理这个信息,导致照片“躺平”或“倒立”,用 exif - js + canvas 就能解决:

// 封装一个“校正照片方向”的函数
function fixImageOrientation(file, orientation) {
  return new Promise((resolve) => {
    const img = new Image();
    img.src = URL.createObjectURL(file); // 临时预览URL
    img.onload = function() {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      let width = img.width;
      let height = img.height;
      // 根据 orientation 调整 canvas 尺寸(比如旋转后宽高互换)
      if (orientation === 6 || orientation === 8) {
        canvas.width = height;
        canvas.height = width;
      } else {
        canvas.width = width;
        canvas.height = height;
      }
      // 根据 orientation 旋转/翻转 canvas
      switch(orientation) {
        case 2: ctx.scale(-1, 1); break; // 水平翻转
        case 3: ctx.rotate(Math.PI); break; // 转180度
        case 4: ctx.scale(-1, 1); ctx.rotate(Math.PI); break; // 翻转+转180
        case 5: ctx.rotate(0.5 * Math.PI); ctx.scale(-1, 1); break; // 转90+翻转
        case 6: ctx.rotate(0.5 * Math.PI); break; // 转90度
        case 7: ctx.rotate(0.5 * Math.PI); ctx.scale(-1, 1); break; // 转90+翻转
        case 8: ctx.rotate(-0.5 * Math.PI); break; // 转-90度(逆时针90)
        default: break;
      }
      // 把图片画到 canvas 里
      ctx.drawImage(img, 0, 0);
      // 把校正后的 canvas 转成 blob(方便上传或预览)
      canvas.toBlob((blob) => {
        resolve(blob);
      }, file.type);
    };
  });
}
// 结合 exif - js 使用:
fileInput.addEventListener('change', function(e) {
  const file = e.target.files[0];
  if (!file) return;
  const reader = new FileReader();
  reader.readAsArrayBuffer(file);
  reader.onloadend = function() {
    const buffer = reader.result;
    const tags = EXIF.readFromBinaryFile(buffer);
    const orientation = tags.Orientation || 1; // 没有方向信息就默认不转
    fixImageOrientation(file, orientation).then((fixedBlob) => {
      // fixedBlob 就是校正后的图片二进制数据
      // 可以用 URL.createObjectURL(fixedBlob) 生成预览地址,或者直接上传
      const previewImg = document.createElement('img');
      previewImg.src = URL.createObjectURL(fixedBlob);
      document.body.appendChild(previewImg);
    });
  };
});

场景2:读取照片 GPS 信息,显示拍摄地点

很多旅行博主爱分享“带地理标记的照片”,但 exif 里的 GPS 格式很“反人类”——存的是度分秒,还得判断南北纬、东西经,这时候得写个转换函数:

// 把 exif 里的度分秒转成十进制经纬度
function convertGPS(gpsCoord, ref) {
  const degrees = gpsCoord[0]; // 度
  const minutes = gpsCoord[1]; // 分
  const seconds = gpsCoord[2]; // 秒
  // 度 + 分/60 + 秒/3600 = 十进制
  const decimal = degrees + minutes / 60 + seconds / 3600;
  // ref 是 N/S 或 E/W,南纬、西经要加负号
  return (ref === 'S' || ref === 'W') ? -decimal : decimal;
}
// 读取并转换 GPS 信息
reader.onloadend = function() {
  const buffer = reader.result;
  const tags = EXIF.readFromBinaryFile(buffer);
  // 纬度(GPSLatitude)和纬度参考(GPSLatitudeRef)
  const lat = convertGPS(tags.GPSLatitude, tags.GPSLatitudeRef);
  // 经度(GPSLongitude)和经度参考(GPSLongitudeRef)
  const lng = convertGPS(tags.GPSLongitude, tags.GPSLongitudeRef);
  console.log(`这张照片拍于:纬度${lat},经度${lng}`);
  // 可以结合地图 API(比如高德、百度),在地图上标记这个位置
};

场景3:批量处理多张照片的元数据

如果用户一次选 10 张照片,要批量读取每张的“拍摄设备、时间”,就得用 Promise.all 处理异步:

fileInput.addEventListener('change', function(e) {
  const files = e.target.files;
  if (files.length === 0) return;
  const promises = []; // 存每个文件的解析Promise
  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    promises.push(new Promise((resolve) => {
      const reader = new FileReader();
      reader.readAsArrayBuffer(file);
      reader.onloadend = function() {
        const buffer = reader.result;
        const tags = EXIF.readFromBinaryFile(buffer);
        // 把需要的信息打包返回
        resolve({
          fileName: file.name,
          camera: tags.Model || '未知设备',
          shootTime: tags.DateTimeOriginal || '未知时间'
        });
      };
    }));
  }
  // 等所有文件都解析完,再统一处理结果
  Promise.all(promises).then((results) => {
    console.log('所有照片的元数据:', results);
    // 可以把结果渲染到页面,比如做个表格展示
  });
});

避坑指南:这些问题新手常栽跟头!

用 exif - js 时,不少同学会碰到“代码报错”“功能没效果”的情况,提前避开这些坑,开发更顺畅~

坑1:浏览器兼容性差,IE 完全不支持

exif - js 依赖 FileReaderArrayBuffer 这些 HTML5 API,IE 10 及以下完全不支持,Edge 老版本也可能有问题,如果项目必须兼容旧浏览器:

  • 给用户提示“请使用 Chrome、Edge 新版或 Firefox 等现代浏览器”;

  • 或者把“读取 exif”的逻辑放到后端(用 Node.js 库,exif - parser),前端只负责上传文件。

坑2:大图片解析超时、页面卡顿

如果用户上传几十 M 的原图,JS 读 ArrayBuffer 和解析 exif 会特别慢,甚至让页面卡死,解决办法:

  • 在页面加加载动画,让用户知道“系统在处理,别着急”;

  • 限制用户上传图片大小(比如前端用 file.size 判断,超过 5M 就提示“请上传更小的图片”);

  • Web Worker 把解析逻辑放到后台线程,避免阻塞 UI(但代码复杂度会高一些)。

坑3:图片根本没有 exif 数据,代码报错

不是所有图片都有 exif 信息!比如电脑上截图生成的图片、PS 保存时没保留元数据的图片,解析后 tags 可能是 undefined 或空对象,这时候要加容错逻辑

reader.onloadend = function() {
  const buffer = reader.result;
  const tags = EXIF.readFromBinaryFile(buffer) || {}; // 防止tags是null
  const orientation = tags.Orientation || 1; // 没有方向就默认不转
  const cameraModel = tags.Model || '这张图没有设备信息';
};

坑4:Uncaught TypeError: 无法读取 null 的属性

这个错误常见于“用户没选文件就触发读取”,或者“选了非图片文件(txt)”,解决方法:

  • 选文件后,先判断 file 是否存在(if (!file) return;);

  • 文件类型验证,只处理图片:

fileInput.addEventListener('change', function(e) {
  const file = e.target.files[0];
  if (!file || !file.type.startsWith('image/')) { // 只处理图片类型
    alert('请选择图片文件~');
    return;
  }
  // 后续逻辑...
});

坑5:移动端浏览器读取 GPS 失败

部分安卓浏览器对照片 GPS 信息的读取有限制,或者用户没给定位权限,导致 GPSLatitude 这类字段拿不到,应对方案:

  • 前端加权限提示,引导用户开启定位;

  • 如果前端实在拿不到 GPS,就把图片上传到后端,用后端代码读取 exif(后端处理兼容性更好)。

看完这些,你应该对 exif - js 是干啥的、咋用、碰到问题咋解决,有了清晰思路,其实核心就是“让前端能拿到照片的隐藏信息,再根据需求做处理”——不管是校正方向、展示参数还是标记位置,思路都是“读 exif → 处理数据 → 落地场景”,现在可以动手试试,比如给你项目里的图片上传功能加个“自动转正”或者“显示拍摄设备”的小功能,体验下 exif - js 的威力~

您的支持是我们创作的动力!
温馨提示:本文作者系Terry ,经Web前端之家编辑修改或补充,转载请注明出处和本文链接:
https://jiangweishan.com/article/javascripsjdfh23523.html

网友评论文明上网理性发言 已有0人参与

发表评论: