exif-parser、image-size、image-type 和 imageinfo 都是用于在 Node.js 环境中解析图片二进制数据的工具库,但它们的侧重点各不相同。image-type 专注于通过文件头魔数(Magic Numbers)快速识别图片格式;image-size 专注于读取图片的宽高等尺寸信息;exif-parser 专门用于提取 JPEG 和 TIFF 文件中的 EXIF 元数据(如相机型号、拍摄时间);而 imageinfo 是一个较早期的库,试图同时提供格式、尺寸和部分元数据信息。在现代前端架构中,开发者通常需要根据具体需求组合使用这些工具,而不是依赖单一解决方案。
在处理图片上传、 CDN 优化或媒体库管理时,我们通常不需要解码整个图片文件,只需读取文件头部的少量二进制数据即可获取关键信息。exif-parser、image-size、image-type 和 imageinfo 都解决了这个问题,但它们的分工不同。让我们从实际工程角度对比它们的用法和适用场景。
这四个包的核心区别在于它们从二进制数据中提取了什么信息。
image-type 专注于识别文件格式。
// image-type: 仅识别格式
const imageType = require('image-type');
const buffer = fs.readFileSync('photo.jpg');
const type = imageType(buffer);
// 输出: { ext: 'jpg', mime: 'image/jpeg' } 或 null
image-size 专注于读取物理尺寸。
// image-size: 读取尺寸
const sizeOf = require('image-size');
const buffer = fs.readFileSync('photo.jpg');
const dimensions = sizeOf(buffer);
// 输出: { width: 1920, height: 1080, type: 'jpg' }
exif-parser 专注于提取 EXIF 元数据。
// exif-parser: 提取元数据
const ExifParser = require('exif-parser');
const buffer = fs.readFileSync('photo.jpg');
const parser = ExifParser.create(buffer);
const tags = parser.parse();
// 输出: { tags: { Make: 'Canon', DateTime: '2023:01:01...' } }
imageinfo 试图一站式解决。
// imageinfo: 混合信息
const imageinfo = require('imageinfo');
const buffer = fs.readFileSync('photo.jpg');
const info = imageinfo(buffer);
// 输出: { format: 'JPEG', width: 1920, height: 1080, ... }
在现代开发中,API 的直观性和同步/异步行为直接影响代码结构。
image-type 提供极简的同步 API。
// image-type: 同步校验
if (!imageType(buffer)) {
throw new Error('Invalid image file');
}
image-size 同样支持同步调用。
// image-size: 支持路径或 Buffer
const dimensions = sizeOf('path/to/image.png');
console.log(dimensions.width, dimensions.height);
exif-parser 采用链式调用设计。
// exif-parser: 链式调用
const tags = ExifParser.create(buffer)
.parse()
.get('tags');
imageinfo 是最传统的函数式 API。
// imageinfo: 传统调用
const result = imageinfo(buffer);
if (!result.format) {
// 处理未知格式
}
在处理用户上传图片时,验证文件真实性至关重要。
image-type 是防止文件伪装的首选。
.exe 重命名为 .jpg,但文件头不会变。// image-type: 安全验证
const type = imageType(buffer);
const allowed = ['jpg', 'png', 'gif'];
if (!type || !allowed.includes(type.ext)) {
return res.status(400).send('Invalid file type');
}
image-size 也可用于验证,但侧重不同。
// image-size: 可用性验证
try {
const dims = sizeOf(buffer);
if (dims.width === 0 || dims.height === 0) {
throw new Error('Empty image');
}
} catch (e) {
return res.status(400).send('Corrupted image');
}
exif-parser 不适合用于安全验证。
// exif-parser: 仅用于信息提取
const tags = ExifParser.create(buffer).parse();
// 即使不是合法图片,只要头部像 JPEG 就可能尝试解析
imageinfo 提供基础验证。
// imageinfo: 基础验证
const info = imageinfo(buffer);
if (!info.format) {
// 可能误判 WebP 或 AVIF 为无效
}
选择库时,维护状态决定了长期项目的稳定性。
image-size 和 image-type 是目前的主流选择。
// 现代组合方案
import imageType from 'image-type';
import sizeOf from 'image-size';
// 先验类型,再取尺寸
const type = imageType(buffer);
const size = sizeOf(buffer);
exif-parser 处于维护模式。
// 特定场景仍可使用
import ExifParser from 'exif-parser';
// 适用于需要读取相机方向标签以自动旋转图片的场景
imageinfo 已不再推荐用于新项目。
// 不推荐:遗留代码可见
import imageinfo from 'imageinfo';
// 建议迁移至 image-size + image-type 组合
需要验证文件是图片,且尺寸不能太大。
image-type + image-sizeimage-type 确保安全,image-size 确保尺寸符合 UI 要求。// 头像上传校验
const type = imageType(buffer);
const size = sizeOf(buffer);
if (type.ext !== 'png' || size.width > 500) {
throw new Error('Invalid avatar');
}
需要显示相机型号和拍摄参数。
exif-parser// 提取摄影参数
const tags = ExifParser.create(buffer).parse().tags;
const camera = `${tags.Make} ${tags.Model}`;
旧代码库中已经使用了 imageinfo。
// 遗留系统
const info = imageinfo(buffer);
// 监控日志,记录无法识别的格式
| 特性 | image-type | image-size | exif-parser | imageinfo |
|---|---|---|---|---|
| 主要用途 | 格式识别 | 尺寸读取 | EXIF 元数据 | 混合信息 |
| 支持格式 | 多 (含 WebP) | 多 (含 WebP) | JPEG, TIFF | 较少 (旧格式) |
| 返回数据 | ext, mime | width, height, type | tags (Make, Model...) | format, width, height |
| 维护状态 | ✅ 活跃 | ✅ 活跃 | ⚠️ 稳定 | ❌ 停滞 |
| 推荐指数 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
在现代前端架构中,单一职责原则同样适用于工具库的选择。
不要试图用一个库解决所有问题。image-type 负责安全校验,image-size 负责布局计算,exif-parser 负责元数据展示。这种组合方式虽然引入了多个依赖,但每个库都更专注、更稳定,且更容易单独替换。
对于新项目,请直接避开 imageinfo,采用 image-type 和 image-size 的组合。这不仅能获得更好的 TypeScript 支持,还能确保你的应用在未来几年内都能正确处理新兴的图片格式。
当你需要读取图片的 EXIF 元数据(如相机型号、GPS 位置、拍摄时间)时选择此包。它专门针对 JPEG 和 TIFF 格式优化,适合需要处理照片方向校正或归档元数据的场景。注意它不提供通用的图片尺寸读取功能,且库本身较旧,维护频率低。
当你需要快速获取图片的宽度和高度时选择此包。它支持多种格式(PNG, JPG, GIF, WebP 等),API 简单同步,性能优秀。它是目前 Node.js 生态中获取图片尺寸的事实标准,适合用于图片上传验证或响应式布局计算。
当你需要验证文件扩展名是否与真实文件内容匹配时选择此包。它通过检查文件头魔数来识别格式,不依赖文件扩展名。适合用于安全校验,防止用户上传恶意文件伪装成图片,但不提供尺寸或元数据信息。
不建议在新项目中使用。虽然它能同时提供格式和尺寸信息,但该库已多年未更新,支持的格式有限且缺乏维护。在现代架构中,建议组合使用 image-size 和 image-type 以获得更好的稳定性和社区支持。
exif-parser is a parser for image metadata in the exif format, the most popular metadata format for jpeg and tiff images. It is written in pure javascript and has no external dependencies. It can also get the size of jpeg images and the size of the jpeg thumbnail embedded in the exif data. It can also extract the embedded thumbnail image.
npm install exif-parser
You can also build a browser bundle to include it with a <script> tag in a HTML document, like this:
git clone git@github.com:bwindels/exif-parser.git
cd exif-parser/
make build-browser-bundle
Built versions of the bundles are also available in the exif-parser-browser-bundles repo.
This will generate a dist/exif-parser-(version).js and dist/exif-parser-(version)-min.js file. These bundles expose the parser on the ExifParser global variable, which you would use like this:
var parser = window.ExifParser.create(arrayBuffer);
To start parsing exif data, create a new parser like below. Note that the buffer you pass does not have to be the buffer for the full jpeg file. The exif section of a jpeg file has a maximum size of 65535 bytes and the section seems to always occur within the first 100 bytes of the file. So it is safe to only fetch the first 65635 bytes of a jpeg file and pass those to the parser.
The buffer you pass to create can be a node buffer or a DOM ArrayBuffer.
var parser = require('exif-parser').create(buffer);
var result = parser.parse();
Before calling parse, you can set a number of flags on the parser, telling it how to behave while parsing.
Add fields in the binary format to result. Since these fields are mostly used for internal fields like Padding, you generally are not interested in these. If enabled, values for these fields will be a Buffer object in node or an ArrayBuffer in DOM environments (browsers).
parser.enableBinaryFields([boolean]), default false;
EXIF tags are organized into different sections, and to tell you the offset to other sections, EXIF uses certain tags. These tags don't tell you anything about the image, but are more for parsers to find out about all tags. Hence, these "pointer" fields are not included in the result tags field by default. Change this flag to include them nonetheless.
parser.enablePointers([boolean]), default false;
Resolve tags to their textual name, making result.tags a dictonary object instead of an array with the tag objects with no textual tag name.
parser.enableTagNames([boolean]), default true;
Read the image size while parsing.
parser.enableImageSize([boolean]), default true;
Read the EXIF tags. Could be useful to disable if you only want to read the image size.
parser.enableReturnTags([boolean]), default true;
EXIF values can be represented in a number of formats (fractions, degrees, arrays, ...) with different precision. Enabling this tries to cast values as much as possible to the appropriate javascript types like number, Date.
parser.enableSimpleValues([boolean]), default true;
the tags that were found while parsing are stored in result.tags unless you set parser.enableReturnTags(false). If parser.enableTagNames is set to true, result.tags will be an object with the key being the tag name and the value being the tag value. If parser.enableTagNames is set to false, result.tags will be an array of objects containing section, type and value properties.
If parser.enableImageSize is set to true, result.getImageSize() will give you the image size as an object with width and height properties.
You can check if there is a thumbnail present in the exif data with result.hasThumbnail(). Exif supports thumbnails is jpeg and tiff format, though most are in jpeg format. You can check if there is a thumbnail present in a give format by passing the mime type: result.hasThumbnail("image/jpeg").
You can also get the image size of the thumbnail as an object with width and height properties: result.getThumbnailSize().
To get the node buffer or arraybuffer containing just the thumbnail, call result.getThumbnailBuffer()
Install nodeunit globally from npm if you haven't done so already.
You can run the tests with nodeunit test/test-*.js.
I welcome external contributions through pull requests. If you do so, please don't use regular expressions. I don't like them, and don't want to maintain a project where they are used. Also, when fixing a bug please provide a regression unit test if it makes sense.