天,请注意时效性苹果相机在 iOS 11 之后,将相机默认的格式设置为了 HEIC,据说可以在不缩减照片质量的前提下,大幅缩减照片体积,你可以在 设置-相机-格式
来查看,「高效」表示的就是 HEIC 格式,「兼容性最佳」表示的是原始的 JEPG 格式。关于 HEIC 格式更多介绍可以看 这里。
由此带来的问题是,目前所有的 Web 浏览器都不支持显示 HEIC 格式的图片,因此如何转换该格式的图片就成为了一个问题。
我经常将苹果相册中的图片直接从相册中拖拽或复制粘贴到 Craft 中,因此如果直接将该格式的图片上传到个人博客的话,是无法显示出来的,因此必须转换成通用格式如 PNG 或者 JPEG,这里我选择使用 Sharp 库进行转换,将其转换成了 Webp(因为 HEIC 转 PNG 太大了)。
如果仅仅转换普通的格式如 JPEG 到 PNG,那么是没有什么问题的,但是如果是转换 HEIC 到其他格式,那麻烦就来了,因为这个过程 Sharp 依赖一个平台原生的工具库,叫 libvips
:
来处理 HEIC 格式的文件,因此如果你本地没有全局安装这个工具,那么处理 HEIC 格式的图片的时候就会遇到下面的错误:
(node:11469) UnhandledPromiseRejectionWarning: Error: source: bad seek to 807962
heif: Unsupported feature: Unsupported codec (4.3000)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:11469) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
说是无法处理 HEIC 格式的文件。
遇到问题第一时间当然是去官方仓库的 issue 查找相关解决办法,果然有人遇到类似的问题:
于是按照他的说法,我使用 homebrew
安装了 libvips
:
这个过程需要挺久,会一直有 log 出现,耐心等待完成无报错即可。
然后满怀信心的再次安装 Sharp npm install sharp
期待能看到这条信息:
sharp: Detected globally-installed libvips v8.13.3
出现这条信息表明 Sharp 的安装脚本检测到本地安装了 libvips,但是当我再次安装 Sharp 的时候,它依然用的是已经下好的缓存:
> sharp@0.31.3 install /Users/x/Code/test2/node_modules/sharp
> (node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)
sharp: Using cached /Users/x/.npm/_libvips/libvips-8.13.3-darwin-x64.tar.br
sharp: Integrity check passed for darwin-x64
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN test2@1.0.0 No description
npm WARN test2@1.0.0 No repository field.
added 45 packages from 206 contributors and audited 45 packages in 4.5s
10 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
于是我删除了上面 log 中缓存的文件 rm -rf /Users/x/.npm/_libvips/
目录,然后再次执行,依然 无法使用系统版本的 libvips:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> sharp@0.31.3 install /Users/x/Code/test2/node_modules/sharp
> (node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)
sharp: Downloading https://github.com/lovell/sharp-libvips/releases/download/v8.13.3/libvips-8.13.3-darwin-x64.tar.br
sharp: Integrity check passed for darwin-x64
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN test2@1.0.0 No description
npm WARN test2@1.0.0 No repository field.
added 45 packages from 206 contributors and audited 45 packages in 10.727s
10 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
于是我决定 debug 这个包。
首先从 log 开始,随便找一个看上去像是版本判断的 log,如 Using cached
作为关键字,在 node_modules/sharp
包中搜索相关关键字,在 sharp/install/libvips.js
中发现如下代码:
1
libvips.log(`Using cached ${tarPathCache}`);
于是继续寻找,发现这个函数顶上有一个决定是使用系统内置的 libvips 还是它自己编译的不支持 HEIC 格式的二进制包的逻辑:
1
const useGlobalLibvips = libvips.useGlobalLibvips();
基本就是这里了,debug 了这个函数,发现在判断 isRosetta
的时候,返回了 true。发现如果是返回 false,就会使用系统内置的 libvips,否则就使用它自己编译的 libvips:
这里出现了一个奇怪的情况是,在 debug 的时候,process.arch 输出的是 x64
,且 log 这个代码输出的是 sysctl.proc_translated: 1
:
但是直接在控制台执行该代码,输出的是 sysctl.proc_translated: 0
:
这里我就得出结论,肯定是 node 的版本问题导致了它使用了自己编译的 libvips。于是我使用 nvm,将默认 node 版本切换到 arm64 版本后,再次执行 npm i sharp
,问题解决。
下面是这个过程可能用到的代码:
1
2
3
4
5
6
7
8
9
10
// 列出 nvm 版本
nvm list
// 输出当前电脑的架构
arch // 如果输出的是 arm64 表示就是 M1 芯片
// 使用最新的稳定版
nvm install stable // 我的机器给我安装了 19.3.0 的 arm64 版本的 node
// 将该 node 作为默认
nvm alias default v19.3.0
// 在当前终端使用 19.3.0 版本的 node
nvm use 19.3.0
随后给作者提了个 PR,来修改 isRosetta 的逻辑:
但是作者解释说这个函数这么做是有原因的,因此我建议他在安装 Sharp 的时候提醒用户并未使用全局安装的 libvips 包的原因,最终作者采纳并接受了我的意见: