使用 JS 操作 mac 下文件拓展属性

任何可以使用 JavaScript 来实现的应用都最终都会使用 JavaScript. --Atwood

通过浏览器和 Node.js,JavaScript 可以说上天入地无所不能。现在许多客户端应用程序也使用 Electron 进行开发。但是 Node.js 本身在一些 native 操作上稍有欠缺,比如说 mac 下文件拓展属性。

什么是拓展属性

拓展属性是 mac 下文件系统提供的一种机制。除了基本的文件属性(修改时间、创建时间等),mac 还允许用户和程序自定义属性。这个属性是一个以 key-value 形式储存的键值对。key 是 UTF-8 字符串,value 则是任意形式的数据。

Mac 下一些系统程序也依赖这个拓展属性。比如说我们去 Node.js 官网下载一个 pkg 安装包,然后打开,mac 会弹出这个框。

quarantine

你可能会好奇,这个对话框上信息很全呀,系统怎么知道我是用 Chrome 从 nodejs.org 下载的呢?实际上是因为 Chrome 在你下载的这个 pkg 文件上设置了一个叫 com.apple.quarantine 的属性。怎么查看这个属性呢,其实可以用命令行看到:

xattr -l node-v13.9.0.pkg

screenshot 1

如果你使用命令 xattr 把属性去掉,系统则不会再弹框了:

xattr -d com.apple.quarantine your_file

com.apple.metadata:kMDItemWhereFroms 这个属性就告诉了我们这个文件是从哪里来的。像上图中的文件是我从浏览器下载的,就储存了这个文件下载的 URL。

比如你使用 AirDrop 传输文件,这个属性就会储存你的来源设备:

像上图,我用 iPhone 传了一张图片,属性里面储存了我的 iPhone 的设备名字。属性显示这个文件是从 sharingd 来的,而 sharingd 就是 AirDrop 的后台进程。所以对于一些文件来说,读取这个拓展属性可以知道这个文件的来源。在一些操作中,这个信息还是挺有用的。

而这个 kMDItemWhereFroms 属性的格式则是 mac 下比较常见的 plist 格式,下文会讲解如何读取这个格式。

拓展属性它还可以为文件设置自定义图标,比如说你可以为普通文件和文件夹设置你喜欢的图标,这个图标的内容就储存在拓展属性里面:

custom icon screenshot

更多关于拓展属性的用法大家可以自行去搜索资料了解,下文讲讲如何在 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 原生化功能。