
不少做前端开发的同学,碰到需要读取照片拍摄参数、处理图片方向这些需求时,总会疑惑“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 依赖 FileReader、ArrayBuffer 这些 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 的威力~






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