item API:读取、修改、删除素材
eagle.item 是插件最常用的模块。素材在官方文档里的原词是「项目」(Item),一个 Item 既可以是图片,也可以是视频、音频、PSD、SVG、书签,甚至一段网页剪藏。理解这一点很重要——很多「图片处理插件」的 API 写法,其实对所有素材类型都成立。
核心设计:读回来的对象是活的
这是 item API 最需要记住的一件事:从 API 取回的 Item 是内存中的对象引用,不是快照。你直接改属性,再调 save() 落盘:
let item = await eagle.item.getById('item_id');
item.name = 'New Name';
item.tags = ['tag_1', 'tag_2'];
await item.save(); // 必须 save 才生效
这个设计的理由很实际:Eagle 的资源库数据存在 metadata.json 里,由主应用统一管理并发。如果 API 要求「传入完整的修改后对象」,调用方就得先深拷贝、比对、再整体覆写,极其容易丢字段。改成「引用 + save」,主应用就能精确追踪哪些字段变了。
代价是:忘了 save() 改动就白改,而且不会有任何报错。
读取:按需选择粒度
get() 是最通用的读取方法,条件全部可选,组合起来就是筛选器:
// 只要 jpg
let jpgs = await eagle.item.get({ ext: "jpg" });
// 只要选了某个标签的素材,并且只取部分字段
let itemsWithFields = await eagle.item.get({
tags: ["Design"],
fields: ["id", "name", "tags", "modifiedAt"]
});
fields 参数是个性能利器。素材库里可能有十万条记录,全量返回会构造十万个完整对象。官方提示说,用它可以显著提升性能,特别是在处理大量文件时只需要部分信息的场景。
shape 参数的值域是:square、portrait、panoramic-portrait、landscape、panoramic-landscape——这几个是长宽比分类。
其余读取方法各有分工:
let one = await eagle.item.getById('item_id');
let many = await eagle.item.getByIds(['item_id_1', 'item_id_2']);
let selected = await eagle.item.getSelected();
// 只要数量时用 count,不要用 get().length
let jpgCount = await eagle.item.count({ ext: "jpg" });
let totalCount = await eagle.item.countAll();
let selectedCount = await eagle.item.countSelected();
增量同步专用
后台服务插件做增量同步时,用这个方法拿 ID 和修改时间,比全量 get() 快得多:
let idsWithTime = await eagle.item.getIdsWithModifiedAt();
// [{ id: "ITEM_ID_1", modifiedAt: 1625123456789 }, ...]
let changedItems = idsWithTime.filter(item => item.modifiedAt > lastSyncTime);
这是同步类插件的核心 API:每轮只处理上次之后变过的素材。
新增素材:四个 add 方法
// 从网络链接添加
const itemId = await eagle.item.addFromURL(imgURL, {
name: 'Camping',
website: 'https://dribbble.com/shots/19744134-Camping-2',
tags: ["Dribbble", "Illustration"],
folders: [],
annotation: 'add from eagle api',
});
// 从 base64 添加
const itemId2 = await eagle.item.addFromBase64(base64, {
name: 'Illustration Logo',
tags: ["Adobe", "Logo"],
});
// 从本地路径添加
const itemId3 = await eagle.item.addFromPath(filePath, {
name: 'Logo',
tags: ["Adobe", "Logo"],
});
// 添加书签
const itemId4 = await eagle.item.addBookmark('https://www.google.com/', {
name: 'Eagle',
tags: ["Eagle", "Site"],
});
四个方法的 options 结构基本一致:name 文件名、website 来源网址、tags 标签数组、folders 文件夹 ID 数组、annotation 注释。区别在于 addBookmark 多一个 base64 字段用于自定义缩略图,因为它本身没有图像内容可抓。
addFromURL 支持 http、https、base64 三种形式的 URL,这是把网络图片收进素材库最直接的路径。
删除与打开
let item = await eagle.item.getById('item_id');
// 丢到垃圾桶(不是彻底删除)
await item.moveToTrash();
// 在全部列表显示
await item.open();
await item.open({ window: true }); // 在新窗口打开,需要 4.0 build12+
// 静态方法等价写法
await eagle.item.open('item_id');
await eagle.item.select(['ITEM_ID_1', 'ITEM_ID_2']);
moveToTrash() 把素材移到回收站,这是唯一官方提供的删除方式——没有「彻底删除且不可恢复」的 API。这个设计是刻意的:插件不应该有能力做不可逆删除。想做批量清理,就一批一批 moveToTrash()。
实例方法速查
let item = await eagle.item.getById('item_id');
// 替换文件本体(自动刷新缩略图)
await item.replaceFile('new_file_path');
// 重新刷新缩略图,同时重算尺寸、颜色、大小
await item.refreshThumbnail();
// 设置自定义缩略图
await item.setCustomThumbnail('thumbnail_path');
// 标注相关(需要 Eagle 4.0 build22+)
await item.addComment({ x: 350, y: 480, width: 380, height: 400, annotation: "脸部区域" });
await item.addComment({ duration: 65.5, annotation: "重要场景" });
await item.updateComment('comment_id', { annotation: "更新后的文字" });
await item.removeComment('comment_id');
// 只选中自己
await item.select();
replaceFile() 有个重要特性:会自动刷新缩略图,不需要再调 refreshThumbnail()。但官方给的建议值得照做——先把新文件保存到其他路径,确认无误后再替换。直接原地覆盖一旦中途出错,文件就损毁且无法复原。
实例属性:只读 vs 可写
这个区分很重要,因为给只读属性赋值不会报错,只是静默无效:
可写属性(改了记得 save()):name、url、annotation、tags、folders、star、width、height、importedAt。
只读属性(读了能用,赋值无效):id、ext、size、modifiedAt、isDeleted、palettes、comments、noThumbnail、noPreview、filePath、fileURL、thumbnailPath、thumbnailURL、metadataFilePath。
几个高频使用的说明:
star是评分字段,取值 0 到 5。设成 0 相当于取消评分。importedAt可写但值必须是正整数时间戳,无效值会被忽略,需要 4.0 build18 以上。thumbnailURL返回file:///链接,要在 HTML 里显示缩略图就直接用它。metadataFilePath存在,但官方反复强调不要直接改这个文件,要用save()。
批量操作的正确写法
// 危险:一次处理十万条会卡死界面
const all = await eagle.item.getAll();
for (const item of all) {
item.tags = ['processed'];
await item.save();
}
// 安全:分批 + 让出主线程
const all = await eagle.item.get({ fields: ["id", "name", "tags"] });
const BATCH = 50;
for (let i = 0; i < all.length; i += BATCH) {
const slice = all.slice(i, i + BATCH);
for (const item of slice) {
item.tags = [...new Set([...item.tags, 'processed'])];
await item.save();
}
// 每批之间给 UI 一个呼吸的机会
await new Promise(r => setTimeout(r, 0));
const progress = Math.round((i + BATCH) / all.length * 100);
console.log(`进度 ${progress}%`);
}
这里有两个必须记住的点。第一,用 fields 限制返回字段,因为批量修改只需要 id 和 tags。第二,getAll() 在资源库规模大(官方举例 20 万条以上)时不要无限制调用,会明显拖低应用性能。
开发流程
- 先用
eagle.item.getSelected()确认能读到数据。 - 确定业务需要哪些字段,用
fields参数把查询裁剪到最小。 - 改属性后立刻
await save(),再更新界面。 - 批量操作前用
count()探规模,超过一千条就分批处理。 - 删除只用
moveToTrash(),并在 UI 上确认。 - 全程用
eagle.log记录关键分支。
常见坑
改完属性不调
save()。 界面上看着「改不动」,重开窗口又变回去了。这是最常见的问题,而且不报错。养成「改完必 save」的习惯。批量操作时忘了给事件循环让路。 几千个
await save()连续执行会长时间占住主线程,用户看到的是 Eagle 卡死无响应。分批加setTimeout让出线程,界面才不会僵。延伸注意: * 给只读属性赋值以为改成功了。 比如
item.size = 0、item.ext = 'png'、item.filePath = ...,都不会生效也不报错。要改尺寸用width/height,要改扩展名就替换文件本体。 * 想找「彻底删除」方法。 没有这个 API。只能用moveToTrash(),素材进回收站后由用户在 Eagle 里决定要不要永久清除。整理人:WorkBuddy
