任何可以使用 JavaScript 来实现的应用都最终都会使用 JavaScript. --Atwood
通过浏览器和 Node.js,JavaScript 可以说上天入地无所不能。现在许多客户端应用程序也使用 Electron 进行开发。但是 Node.js 本身在一些 native 操作上稍有欠缺,比如说 mac 下文件拓展属性。
什么是拓展属性
拓展属性是 mac 下文件系统提供的一种机制。除了基本的文件属性(修改时间、创建时间等),mac 还允许用户和程序自定义属性。这个属性是一个以 key-value 形式储存的键值对。key 是 UTF-8 字符串,value 则是任意形式的数据。
Mac 下一些系统程序也依赖这个拓展属性。比如说我们去 Node.js 官网下载一个 pkg 安装包,然后打开,mac 会弹出这个框。
你可能会好奇,这个对话框上信息很全呀,系统怎么知道我是用 Chrome 从 nodejs.org 下载的呢?实际上是因为 Chrome 在你下载的这个 pkg 文件上设置了一个叫 com.apple.quarantine 的属性。怎么查看这个属性呢,其实可以用命令行看到:
xattr -l node-v13.9.0.pkg
如果你使用命令 xattr 把属性去掉,系统则不会再弹框了:
xattr -d com.apple.quarantine your_file
像 com.apple.metadata:kMDItemWhereFroms
这个属性就告诉了我们这个文件是从哪里来的。像上图中的文件是我从浏览器下载的,就储存了这个文件下载的 URL。
比如你使用 AirDrop 传输文件,这个属性就会储存你的来源设备:
像上图,我用 iPhone 传了一张图片,属性里面储存了我的 iPhone 的设备名字。属性显示这个文件是从 sharingd 来的,而 sharingd 就是 AirDrop 的后台进程。所以对于一些文件来说,读取这个拓展属性可以知道这个文件的来源。在一些操作中,这个信息还是挺有用的。
而这个 kMDItemWhereFroms 属性的格式则是 mac 下比较常见的 plist 格式,下文会讲解如何读取这个格式。
拓展属性它还可以为文件设置自定义图标,比如说你可以为普通文件和文件夹设置你喜欢的图标,这个图标的内容就储存在拓展属性里面:
更多关于拓展属性的用法大家可以自行去搜索资料了解,下文讲讲如何在 JS 里面操作拓展属性。
使用 JS 操作
由于 Node.js 环境下缺乏比较好用的操作 xattr 的库,所以这里推销一下我封装的 node-xattr,功能很全很强大。使用 N-API,支持同步异步操作、设置自定义 icon,解析 plist 等实用的功能。同时支持 TypeScript,提高开发效率。
安装
yarn add node-xattr
获取 xattr
// 同步版本
const buffer = getXattrSync('./test.txt', 'key'); //获取 Buffer
const string = getXattrSync('./test.txt', 'key', 'utf8'); // 获取 string
// 异步版本
getXattr('./test.txt', 'key').then(buffer => console.log(buffer)).catch(err => console.error(err));
getXattr('./test.txt', 'key', 'utf8').then(str => console.log(str)).catch(err => console.error(err));
设置 xattr
setXattrSync('./test.txt', 'key', 'value');
setXattr('./test.txt', 'key', 'value').catch(err => console.error(err));
设置自定义图标
node-xattr 提供了简单的 API 可以直接设置自定义图标,不需要去了解系统的储存格式。
const { macUtils } = require('node-xattr');
macUtils.setCustomIconSync(TestFile, iconPath);
macUtils.setCustomIcon(TestFile, iconPath).catch(err => console.log(err));
解析 plist
目前来说 node-xattr 提供了简单的两个函数,可以用于解析 kMDItemWhereFroms 这个字段。
const { getXattrSync, macUtils } = require('node-xattr');
const buffer = getXattrSync('./test.txt', 'com.apple.metadata:kMDItemWhereFroms'); //获取 Buffer
const list = macUtils.deserializeArrayOfString(buffer);
console.log(list);
总结
node-xattr 这个库提供了操作 xattr 的能力,可以让你完成很多 Node.js 本身做不到的事情。有了这个库,你可以发挥你的想象力,在你的 Node 程序、 Electron 程序里面直接操作拓展属性,实现你的 macOS 原生化功能。