exiftool-vendored.js

exiftool-vendored.js

Node.js 的高性能跨平台 ExifTool 库

exiftool-vendored.js 为 Node.js 提供快速、跨平台的 ExifTool 访问。这个库具有优异的性能和可靠性,支持读写标签、提取嵌入二进制和恢复元数据。它为常见标签提供强大的类型定义,支持 ExifTool 自动更新,并有完善的测试覆盖。该库被 PhotoStructure 等500多个项目用于处理照片和视频元数据。

ExifToolNode.js元数据跨平台图像处理Github开源项目

exiftool-vendored

快速、跨平台的 Node.js 访问 ExifTool 的方式。由 PhotoStructure 构建和支持。

npm 版本 Node.js CI GitHub 问题 已知漏洞

特性

  1. 同类最佳的跨平台性能和可靠性

    这是 PhotoStructure(以及其他 500+ 个项目)用于读取和写入照片和视频元数据的模块。

    与其他 Node.js ExifTool 模块相比,性能提升可达一个数量级

  2. 尽最大努力提取

    • 带有正确时区偏移编码日期
    • 以浮点数表示的纬度和经度(负值表示在子午线以西或以南)
  3. 支持

  4. 健壮的类型定义,涵盖了超过 6,000 种不同相机品牌和型号使用的 99.5% 的顶级标签(参见示例

  5. 自动更新 ExifTool(新版本频繁发布

  6. 强大的测试覆盖,在 macOS、Linux 和 Windows 上进行

安装

 yarn add exiftool-vendored

 npm install --save exiftool-vendored

调试日志

如果遇到任何问题,首先尝试启用日志记录。

您可以通过 ExifToolOptions.logger 提供一个 Logger 实现,或设置环境变量 NODE_DEBUG=exiftool-vendored查看 debuglog() 文档了解更多详情。

关于在 Electron 中使用

由于每个 Electron 应用程序的设置都不同,而且新版本经常有重大变更,请不要通过在此项目上开 GitHub issue 来寻求帮助

请通过 StackOverflow、Electron discord 或其他渠道寻求帮助。

Electron-builder 支持

electron-builder.ymlasarUnpack 中添加以下模式:

- "node_modules/exiftool-vendored.*/**/*"

默认的 exiftoolPath 实现会自动检测 require 路径中的 app.asar,并将其替换为 app.asar.unpacked

Electron-forge 支持

本库的 25.0 版本添加了对 electron-forge 的实验性支持: 将以下元素添加到 ForgeConfig.packagerConfig.extraResource 字符串数组中,主进程应该就能正常工作。

"./node_modules/exiftool-vendored." + (process.platform === "win32" ? "exe" : "pl")

如果您的主进程 fork 了任何 node 子进程,这些子进程中的 process.resourcesPath 将不会被设置,默认的 exiftoolPath 将无法工作。

如果是这种情况,您必须提供正确的 ExifToolOptions.exiftoolPath 实现, 可以通过 process.env 传递 resourcesPath,或使用其他方法。

安装注意事项

  • exiftool-vendored 通过 optionalDependencies 提供适用于您本地平台的 ExifTool 安装。

  • 除非您知道自己在做什么,否则不应将 exiftool-vendored.exeexiftool-vendored.pl 作为项目的直接依赖项。

  • 如果您在最小化的 Linux 发行版上安装,可能需要安装 perl。在 Alpine 上,运行 apk add perl

  • Node.js 的 -slim docker 镜像不包含可用的 perl 构建。请使用非 slim 镜像。详见问题报告。

  • 如果找不到平台正确的供应商模块(exiftool-vendored.exeexiftool-vendored.pl),系统会在您的PATH中搜索exiftool。请注意,目前支持的Linux发行版中存在一些非常旧版本的exiftool,这个库无法与之正常工作。

升级

有关自上次更新以来的重大变更,请参阅CHANGELOG

主版本号升级

如果现有代码可能受到影响,我会提升主版本号。

我曾多次因为其他库的小版本或补丁版本升级而导致代码出错。我认为在代码变更影响分析中保持悲观态度更好:"过度承诺,少量交付"您的破坏性代码变更。

当您升级到新的主版本时,请在验证自己的系统时多加小心,但如果一切仍然正常工作,也不要感到惊讶。

使用方法

ExifTool有许多配置选项,但所有值都有(或多或少合理的)默认设置。

这些默认设置已用于创建exiftool单例。请注意,如果您不使用默认单例,则无需调用.end()方法。

// 为方便起见,我们在这里使用单例: const exiftool = require("exiftool-vendored").exiftool // 验证一切是否正常工作: exiftool .version() .then((version) => console.log(`我们正在运行ExifTool v${version}`))

如果默认的ExifTool构造函数参数不适合您,它只是一个接受选项哈希的类:

const ExifTool = require("exiftool-vendored").ExifTool const exiftool = new ExifTool({ taskTimeoutMillis: 5000 })

您应该只使用导出的默认exiftool单例,或只创建一个ExifTool实例作为单例。

记得对您使用的单例调用.end()方法。

通用API

ExifTool.read()返回一个Promise,解析为Tags实例。请注意,错误可能通过拒绝promise返回,或对于不太严重的问题,通过errors字段返回。

所有其他公共ExifTool方法都返回Promise<void>,如果操作不成功,将拒绝promise。

Tags类型

ExifTool知道如何提取几千种不同的标签字段。

不幸的是,如果Tags接口很全面,TypeScript会崩溃并显示error TS2590: Expression produces a union type that is too complex to represent

相反,我们从超过10,000种不同的数码相机品牌和型号中构建了一个"常见"标签的语料库,其中许多来自ExifTool元数据存储库和<raw.pixls.us>。

以下是一些示例字段:

/** ★☆☆☆ ✔ 示例:200 */ ISO?: number /** ★★★★ ✔ 示例:1920 */ ImageHeight?: number /** ★★★★ ✔ 示例:1080 */ ImageWidth?: number /** ★★★★ ✔ 示例:"image/jpeg" */ MIMEType?: string

星星表示该字段在示例语料库中有值的常见程度。★★★★字段在超过50%的示例中找到。 ☆☆☆☆字段在不到1%的示例中找到。

勾号表示该字段是否在"流行"相机(如最近的尼康、佳能、索尼和苹果设备)中找到。

Tags的注意事项

Tags中的字段并不全面。

仅仅因为Tags接口中缺少某个字段并不意味着该字段在返回的对象中不存在。换句话说,这个库不会排除未知字段。您和您的代码需要自行查找您期望的其他字段,并将其强制转换为更相关的接口。

日志和事件

要启用此库和底层batch-cluster库的跟踪、调试、信息、警告或错误日志记录,请在ExifTool构造函数选项中提供Logger实例。

ExifTool实例通过batch-cluster发出许多生命周期和错误事件

读取标签

exiftool .read("path/to/image.jpg") .then((tags /*: Tags */) => console.log( `制造商:${tags.Make},型号:${tags.Model},错误:${tags.errors}` ) ) .catch((err) => console.error("发生严重错误:", err))

提取嵌入图像

path/to/image.jpg中提取低分辨率缩略图,将其写入path/to/thumbnail.jpg,并返回一个Promise<void>,当图像提取完成时该promise将被履行:

exiftool.extractThumbnail("path/to/image.jpg", "path/to/thumbnail.jpg")

提取Preview图像(仅在某些图像中找到):

exiftool.extractPreview("path/to/image.jpg", "path/to/preview.jpg")

提取JpgFromRaw图像(在某些RAW图像中找到):

exiftool.extractJpgFromRaw("path/to/image.cr2", "path/to/fromRaw.jpg")

path/to/image.jpg中的"tagname"标签提取二进制值,并将其写入dest.bin(该文件不能已存在,且其父目录必须已存在):

exiftool.extractBinaryTag("tagname", "path/to/file.exf", "path/to/dest.bin")

写入标签

请注意,只有部分标签是可写的。请参考文档并查看"Writable"列。

如果您应用格式错误的值或尝试写入不支持的标签,返回的Promise将被拒绝。

对象的值仅支持字符串和数字基本类型。

要向给定文件写入注释,使其显示在Windows资源管理器属性面板中:

exiftool.write("path/to/file.jpg", { XPComment: "这是一个测试评论" })

要将DateTimeOriginal、CreateDate和ModifyDate标签(使用AllDates快捷方式)更改为2016年2月6日16:56 UTC:

exiftool.write("path/to/file.jpg", { AllDates: "2016-02-06T16:56:00" })

要写入特定元数据组的标签,只需在标签名前加上组名。 (TypeScript用户:你需要进行类型转换才能编译。)

exiftool.write("path/to/file.jpg", { "IPTC:CopyrightNotice": "© 2021 PhotoStructure, Inc.", })

要删除一个标签,将值设为null

exiftool.write("path/to/file.jpg", { UserComment: null })

上面的例子删除了与UserComment标签相关的任何值。

始终注意:时区

如果你编辑时间戳标签,请注意exiftool-vendored使用更改后的时间戳标签与GPS值之间的差异来推断时区。

换句话说,如果你只编辑CreateDate而不编辑GPS时间戳,你的时区要么不正确,要么缺失。有关更多信息,请参阅下面的日期部分。

重写标签

你可能会发现一些图像的元数据已损坏,写入新日期或编辑旋转信息等操作可能会失败。ExifTool可以尝试通过将所有元数据重写到新文件中来修复这些图像,同时保留原始图像内容。有关此功能的更多详细信息,请参阅文档

rewriteAllTags返回一个void Promise,如果出现任何错误,该Promise将被拒绝。

exiftool.rewriteAllTags("problematic.jpg", "rewritten.jpg")

ExifTool配置支持(.ExifTool_config

ExifTool有一个广泛的用户配置系统。有几种使用方法:

  1. 将你的用户配置文件放在你的HOME目录中
  2. EXIFTOOL_HOME环境变量设置为包含用户配置的完全限定路径
  3. 在ExifTool构造函数选项中指定:
new ExifTool({ exiftoolEnv: { EXIFTOOL_HOME: resolve("path", "to", "config", "dir") }

资源卫生

使用完毕后调用ExifTool.end()

你必须显式调用任何使用过的ExifTool实例的.end()方法,以允许node正常退出。

ExifTool子进程会消耗系统资源,并且由于Node.js流的工作方式,会阻止node退出

请注意,你不能在process.on("exit")钩子中调用此方法,因为附加到子进程的stdio流无法被unref。(如果有解决方案,请在上述问题中发帖!)

Mocha v4.0.0

如果你使用mocha v4或更高版本,并且没有调用exiftool.end(),你会发现你的测试套件会挂起。相关变更在此处描述,可以通过添加一个after块来关闭测试中使用的ExifTool实例来解决:

after(() => exiftool.end()) // 假设你的单例名为`exiftool`

日期

你所有图像和视频中的日期元数据很可能都是未完全指定的。

图像和视频很少在其日期中指定时区。如果你的所有文件都是在当前时区捕获的,默认使用本地时区是一个安全的假设,但如果你有在世界不同地方捕获的文件,这个假设将不正确。在世界不同地方解析同一文件会导致同一文件的时间不同。

在7.0.0版本之前,应用了启发式1和3。

从7.0.0版本开始,exiftool-vendored使用以下启发式方法。优先级最高的启发式方法返回的值将用作所有尚未指定时区的日期时间标签的时区偏移。

启发式1:显式元数据

如果存在EXIFTimeZoneOffset标签,它将按照规范应用于DateTimeOriginal,如果有两个值,则也应用于ModifyDate标签。如果存在OffsetTimeOffsetTimeOriginalOffsetTimeDigitized,也会被采纳(但很少设置)。

启发式2:GPS位置

如果存在有效的GPS纬度和经度(值为0, 0被视为无效),将使用tz-lookup库来确定该位置的时区名称。

启发式3:UTC时间戳

如果存在GPSDateTimeDateTimeUTC,只要差值有效,就会使用它们与文件中找到的日期之间的差值作为时区偏移。大于14小时的差值被视为无效。

ExifDate和ExifDateTime

由于日期时间具有这种可选设置的时区,并且某些标签只指定日期,此库返回编码日期、一天中的时间或两者都有的类,带有可选的时区和可选的时区偏移ExifDateTimeExifTime。然后由你来确定什么对你的情况是正确的。

还要注意,一些智能手机记录的时间戳具有微秒精度(不仅仅是毫秒!),ExifDateTimeExifTime都有浮点毫秒。

标签

官方 EXIF 标签名采用 Pascal命名法,如 AFPointSelectedISO。(将字段名"修正"为驼峰命名法会导致难看的 aFPointSelectediSO 这样的畸形)。

Tags 接口由 mktags 脚本自动生成,该脚本解析了超过6,000个独特的相机品牌和型号图像,大部分源自ExifTool网站。mktags 对标签进行分组,提取其类型、使用频率和示例值,以便您的IDE可以自动完成。

标记为"★★★★"的标签,如 MIMEType,应该在大多数文件中都能找到。在几千个元数据标签中,需要意识到通常只有不到50个是常见的。您需要进行研究来确定哪些标签适用于您的用途。

请注意,如果解析失败(例如,日期时间字符串),将返回原始字符串。使用代码时应该合理地验证标签的存在性和类型以确保安全。

序列化

ExifTool.read() 返回的 Tags 对象可以使用 JSON.stringify 序列化为JSON。

要重新构建,请使用 parseJSON() 方法。

import { exiftool, parseJSON } from "exiftool-vendored" const tags: Tags = await exiftool.read("/path/to/file.jpg") const str: string = JSON.stringify(tags) // parseJSON 不验证输入,所以我们不断言它是一个 Tags 实例,但你可以(不安全地)将其转换 const tags2: Tags = parseJSON(str) as Tags

性能

默认的 exiftool 单例是故意限制的。如果可以接受充分利用系统资源:

  1. maxProcs 设置得更高

  2. 考虑将 minDelayBetweenSpawnMillis 设为0

  3. 在性能良好的Linux机器上,较小的 streamFlushMillis 值可能也有效:如果您看到 noTaskData 事件,则需要增加该值。

基准测试

yarn mktags ../path/to/examples 目标会读取示例图像和视频目录层次结构中找到的所有标签,并解析结果。

在2019年的AMD Ryzen 3900X上运行Ubuntu 20.04和SSD的 exiftool-vendored v16.0.0版本,每个线程每秒可以处理20多个文件,或者在利用所有CPU线程时每秒可以处理500多个文件。

批处理模式

使用ExifTool的 -stay_open 批处理模式意味着我们可以在多个请求中重复使用单个ExifTool实例,大大降低响应延迟并减少系统负载。

并行性

为避免系统负担过重,exiftool 单例配置的 maxProcs 设置为当前系统CPU数量的四分之一(最少1个);不会生成超过 maxProcs 数量的 exiftool 实例。但是,如果系统CPU受限,您可能需要更小的值。如果您有非常快的磁盘IO,增加 maxProcs 的值可能会提高速度,但请注意每个子进程可能会消耗100MB的RAM。

作者

贡献者 🎉

更新日志

请查看GitHub上的 CHANGELOG

编辑推荐精选

扣子-AI办公

扣子-AI办公

职场AI,就用扣子

AI办公助手,复杂任务高效处理。办公效率低?扣子空间AI助手支持播客生成、PPT制作、网页开发及报告写作,覆盖科研、商业、舆情等领域的专家Agent 7x24小时响应,生活工作无缝切换,提升50%效率!

堆友

堆友

多风格AI绘画神器

堆友平台由阿里巴巴设计团队创建,作为一款AI驱动的设计工具,专为设计师提供一站式增长服务。功能覆盖海量3D素材、AI绘画、实时渲染以及专业抠图,显著提升设计品质和效率。平台不仅提供工具,还是一个促进创意交流和个人发展的空间,界面友好,适合所有级别的设计师和创意工作者。

图像生成AI工具AI反应堆AI工具箱AI绘画GOAI艺术字堆友相机AI图像热门
码上飞

码上飞

零代码AI应用开发平台

零代码AI应用开发平台,用户只需一句话简单描述需求,AI能自动生成小程序、APP或H5网页应用,无需编写代码。

Vora

Vora

免费创建高清无水印Sora视频

Vora是一个免费创建高清无水印Sora视频的AI工具

Refly.AI

Refly.AI

最适合小白的AI自动化工作流平台

无需编码,轻松生成可复用、可变现的AI自动化工作流

酷表ChatExcel

酷表ChatExcel

大模型驱动的Excel数据处理工具

基于大模型交互的表格处理系统,允许用户通过对话方式完成数据整理和可视化分析。系统采用机器学习算法解析用户指令,自动执行排序、公式计算和数据透视等操作,支持多种文件格式导入导出。数据处理响应速度保持在0.8秒以内,支持超过100万行数据的即时分析。

AI工具酷表ChatExcelAI智能客服AI营销产品使用教程
TRAE编程

TRAE编程

AI辅助编程,代码自动修复

Trae是一种自适应的集成开发环境(IDE),通过自动化和多元协作改变开发流程。利用Trae,团队能够更快速、精确地编写和部署代码,从而提高编程效率和项目交付速度。Trae具备上下文感知和代码自动完成功能,是提升开发效率的理想工具。

AI工具TraeAI IDE协作生产力转型热门
AIWritePaper论文写作

AIWritePaper论文写作

AI论文写作指导平台

AIWritePaper论文写作是一站式AI论文写作辅助工具,简化了选题、文献检索至论文撰写的整个过程。通过简单设定,平台可快速生成高质量论文大纲和全文,配合图表、参考文献等一应俱全,同时提供开题报告和答辩PPT等增值服务,保障数据安全,有效提升写作效率和论文质量。

AI辅助写作AI工具AI论文工具论文写作智能生成大纲数据安全AI助手热门
博思AIPPT

博思AIPPT

AI一键生成PPT,就用博思AIPPT!

博思AIPPT,新一代的AI生成PPT平台,支持智能生成PPT、AI美化PPT、文本&链接生成PPT、导入Word/PDF/Markdown文档生成PPT等,内置海量精美PPT模板,涵盖商务、教育、科技等不同风格,同时针对每个页面提供多种版式,一键自适应切换,完美适配各种办公场景。

AI办公办公工具AI工具博思AIPPTAI生成PPT智能排版海量精品模板AI创作热门
潮际好麦

潮际好麦

AI赋能电商视觉革命,一站式智能商拍平台

潮际好麦深耕服装行业,是国内AI试衣效果最好的软件。使用先进AIGC能力为电商卖家批量提供优质的、低成本的商拍图。合作品牌有Shein、Lazada、安踏、百丽等65个国内外头部品牌,以及国内10万+淘宝、天猫、京东等主流平台的品牌商家,为卖家节省将近85%的出图成本,提升约3倍出图效率,让品牌能够快速上架。

下拉加载更多