【CEP教程-14】数据存储相关

【CEP教程-14】数据存储相关

在我们日常插件开发过程中,经常会需要存储一些数据,比如用户当前操作的状态、用户登录的账号信息,或者我们对PSD文档做的一些修改等等,这些都需要我们将一些动态数据,写入到当前用户设备上,这篇文章就来介绍一下CEP插件体系给我们提供了哪些可以进行数据存储的类型和使用方式。

1. 面板状态

如果你什么都不干,面板在每次关闭再打开的时候,都会重新从头开始加载最初的状态,这个会导致用户当前操作的状态丢失,尤其是你用vue/react框架来实现的单页面场景,每次重新打开插件,都会回到最初的界面。

panel state reset

这种情况,我们需要对面板进行持久化操作,代码如下

1
2
3
4
const csInterface = new CSInterface();
const event = new CSEvent("com.adobe.PhotoshopPersistent", "APPLICATION");
event.extensionId = "your.plugin.id"; // 这里填你的插件ID,就是manifest.xml里头的那个
csInterface.dispatchEvent(event);

把这段代码放到页面加载完成的事件中,就可以实现将面板的状态进行持久化,这样你再次打开插件面板的时候,它就能够保持上次关闭的状态了

panel state persistent

需要注意的地方是,如果设置了持久化,它也就不会再重新加载代码了,在我们开发的时候,由于需要不断变更代码,所以不适合设置持久化,一般在插件发布的时候,加上这个。

虽然这个面板状态持久化,并没有涉及到任何数据存储,不过它确实也很重要,所以一并放在这里介绍。

熟悉web开发的小伙伴,应该都知道cookie是什么,起到什么作用。Cookie通常用在我们需要做用户鉴权的的时候,在发送给后端的Http请求头中携带用户的信息,以便后端进行认证。

CEP也提供了Cookie能力,并且提供了两种类型的Cookie:

  1. 会话Cookie,这是一种临时的cookie,当你关闭插件面板之后,它就会失效
  2. 持久化Cookie,它是存在用户本地文件系统的,除非你设置失效,否则会一直存在

持久化Cookie的内容存放在如下位置

CEP 4.x

1
2
Windows: C:\Users\<USERNAME>\AppData\Local\Temp\cep_cookies\
Mac: /Users/<USERNAME>/Library/Logs/CSXS/cep_cookies/

CEP 5.x

1
2
Windows: C:\Users\<USERNAME>\AppData\Local\Temp\cep_cache\
Mac: /Users/<USERNAME>/Library/Logs/CSXS/cep_cache/

CEP 6.x 之后的版本

1
2
Windows: C:\Users\<USERNAME>\AppData\Local\Temp\cep_cache\
Mac: /Users/<USERNAME>/Library/Caches/CSXS/cep_cache/

当你发现Cookie一直都在的时候,可以手动删掉文件夹里头的文件。

Cookie的使用,和web的使用方式一致,通过document.cookie对象进行存取,通常,你都可以去找一些Cookie操作的JS库来简化它的存取。

3. LocalStorage

由于CEP内嵌的浏览器是CEF(Chrome Embed Framework),也支持浏览器的LocalStorage,所以就和Cookie类似,你也可以通过它来进行存储一些Key-Value值,并且它也是持久化存储的,并不会随着插件面板的关闭而失效。

1
2
3
4
5
6
7
// 存储一个值
localStorage.setItem("name", "xiaoqiang");
// 读取一个值
const name = localStorage.getItem("name");
if (name) {
console.log(name);
}

4. FileSystem

Cookie和LocalStorage适合存储一些简单的Key-Value数据,并且不太重要数据项,比如用户的操作状态等等,如果要存储较多、较复杂结构的数据时,就适合存储为本地文件了,比如存储用户登录的一些账户信息,存储用户设置的收藏数据等等。

CEP提供了操作本地文件的API

cep fs

我们可以通过如下示例代码来进行文件、文件夹的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 通过cep的api创建一个文件
var path = "/tmp/test";
var result = window.cep.fs.makedir(path);
if (0 == result.err) {
// success
} else {
// fail
}

// 通过cep的api写入文件
var data = "This is a test.";
var path = "/tmp/test";
var result = window.cep.fs.writeFile(path, data);
if (0 == result.err){
// success
} else {
//
}

上一篇文章我们还介绍了nodejs的使用,里面介绍了通过nodejs提供的fs模块,我们也可以进行本地文件系统的读取操作,详细信息可以看看上一篇文章。

另外, 不止CEP,ExtendScript也提供了FileFolder对象来操作本地文件,这个在之前的文章也介绍过,大家可以翻回去看看,或者查看官方的API文档。 这样,你不仅可以在JS侧进行文件存取,也能在JSX上进行文件的存取,有时候我们也会需要通过这种方式来进行两侧的数据交换。

文件系统操作,有一些需要注意的地方

  1. 权限问题

一般来说,本地文件系统是有严格的权限管理的,所以你不能随便在任何地方进行文件的读写操作,比如window的C盘,mac系统的非用户目录,一般都没有权限进度写入操作。

所以一般推荐插件需要存储的文件,放到用户的%userdata%文件夹下,它的位置在

1
2
windows: C:\Users\<UserName>\AppData\Roaming\
mac: /Users/<UserName>/Library/Application Support/

你可以通过nodejs提供的process.env获取到对应的位置

1
2
3
4
5
6
7
function getUserDirectory() {
if (os() === 'win') {
return path.join(process.env["USERPROFILE"], 'AppData', 'Roaming');
} else {
return path.join(process.env["HOME"], 'Library', 'Application Support');
}
}
  1. 中文路径问题

尤其是windows电脑,很多时候本地路径存在中文,这会导致你在获取路径,存取文件的时候出错,这个时候需要对路径做uriencode进行转义。

  1. 安全问题

有些时候,用户的系统上有杀毒和安全软件,会监听未知来源软件的本地文件修改操作,然后会阻止你的操作,甚至删除你的文件。这种情况不常见,但是遇到了,也没有什么好办法,建议就是不要操作一些不该操作的文件和路径,尽量将文件存储在用户的目录下。

5. XMP MetaData

上面提到的几个存储方案,都是针对面板,或者说针对本机的数据存储。但是有时候,我的插件对PSD文档进行了操作,想要记录一些针对这个PSD文档的数据,这种情况就需要唯一确定这个文档了,然而文档的名称会随时变,文档的ID也会出现重复的情况,没办法通过某个key来唯一确定当前文档,那怎么办呢?

这个时候,我们可以通过将特定的数据,写入这个PSD文件,这样我们就知道这个PSD的信息了!

没错,PSD文件,我们是可以写入数据的!

PSD文件,本身其实是一个巨大的容器,里头存储图像二进制数据,也存储图层结构数据,色彩数据,用户对PSD的操作数据等等,一堆堆,它提供了一个XMP的数据对象,用来给第三方进行数据的存取和写入。于是,我们可以通过它来讲一些数据存在PSD文档当中。

XMP的数据结构,是以XML的格式存储的,我们通过xmpMetadata这个对象可以对它进行存取,示意代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 存储一个key-value
function set(key, value) {
// 这个是它依赖的外部库,需要先判断,再引用
if (ExternalObject.AdobeXMPScript == undefined) {
ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
}
var xmpObject;
try {
var xmp = app.activeDocument.xmpMetadata.rawData;
xmpObject = new XMPMeta(xmp);
} catch (e) {
xmpObject = new XMPMeta();
}
// demo是一个名称空间,用来区分你的数据,和别人的数据
// abc是一个前缀,会在你的设置的数据上都加上此前缀,也是用来区分重名用的
XMPMeta.registerNamespace("demo", "abc");
xmpObject.deleteProperty("demo", key);
xmpObject.setProperty("demo", key, value);
app.activeDocument.xmpMetadata.rawData = xmpObject.serialize();
}

// 读取一个值
function get(key) {
if (ExternalObject.AdobeXMPScript == undefined) {
ExternalObject.AdobeXMPScript = new ExternalObject("lib:AdobeXMPScript");
}
var xmp, xmpObject;
try {
xmp = app.activeDocument.xmpMetadata.rawData;
xmpObject = new XMPMeta(xmp);
} catch (e) {
xmpObject = new XMPMeta();
}
var value = null;
try {
XMPMeta.registerNamespace("demo", "abc");
var property = xmpObject.getProperty("demo", key);
value = property.value;
} catch (e) {
return null;
}
return value;
}

XMP MetaData的操作,官方的文档里头也有详细的介绍,大家可以去翻一番看,通过这个对象,我们可以将数据持久化存储到PSD文档当中,这样无论用户什么时候打开,或者谁打开,我们都能通过读取里头自己设置的数据来辨识它了。

有一个地方需要注意的是,xmp的数据放多了,会导致PSD体积变大,给用户带来困扰。同时有一些用户,或者一些工具会去清理这些数据,降低PSD体积的目的,所以xmp存储的数据,是有丢失的风险的,实际应用的时候需要考虑进去。

总结

本篇文章介绍了CEP面板开发中常见的一些数据存取的方法,不同的方法应用在不同的场景当中,也都是不可或缺的,它使得我们的插件功能更加完善和可靠。

下一篇文章,我们讲插件开发工程化,如何使用现成的web开发框架vue/react来提升面板开发效率,使用Typescript语言来提高编码的健壮性,如何使用webpack来进行文件打包,构建等操作,敬请期待~~

评论