查看: 66|回复: 3

ArkTS素材记录应用:Preferences与沙箱文件存储实践

[复制链接]
发表于 半小时前 | 显示全部楼层 |阅读模式
很多开发者想用HarmonyOS做一个轻量工具,原文给出的素材记录应用就是一个较完整的实践样本。它围绕文字、图片、视频三类素材,使用ArkTS、DevEco Studio、ArkUI Navigation、ArkData Preferences、CoreFileKit fileIo和MediaLibraryKit,完成了从数据模型、存储、页面导航到刷新与媒体选择的闭环。本文按开发实现路径重新梳理其中的可复用方法。

一、需求边界与页面划分
原文作者的场景是记录头条号、公众号素材,因此只保留文字、图片、视频三类内容,并为每条素材增加用户指定的素材日期。应用没有铺开复杂功能,只设计三个页面:Index.ets作为首页列表与导航容器,Edit.ets负责新建和编辑,Detail.ets展示单条素材详情。首页使用Navigation(pathStack)作为根容器,通过.navDestination(this.PageMap)把Detail和Edit映射到对应组件,跳转时调用pathStack.pushPathByName(...)。页面间用activeItemId字段传递当前操作素材id,这种设计适合轻量工具,页面栈和传参关系比较直接。

二、数据模型先定契约
素材类型用MaterialType枚举表示,取值为TEXT、IMAGE、VIDEO,分别对应文字、图片、视频。每条记录用MaterialItem接口描述,包含id、type、title、content、mediaPath、date、createdAt、updatedAt等字段。其中id由generateId()基于时间戳加随机数生成;mediaPath保存媒体文件在应用沙箱中的绝对路径,文字素材为空;date是用户指定的素材日期时间戳;createdAt和updatedAt分别记录创建与最近更新时间;formatDate()和formatDateTime()用于界面展示格式化。先把字段和类型定清楚,后续存储、列表卡片和详情展示才能保持一致。

三、结构化数据用Preferences,媒体文件放沙箱
存储层由MaterialStore单例承担,应用启动时由EntryAbility调用init(context)完成初始化。结构化数据通过@kit.ArkData的preferences存储:整表序列化为JSON字符串写入material_store/records键,读取时再解析回数组,persist()使用putSync加flush落盘。媒体文件则通过@kit.CoreFileKit的fileIo处理。copyMedia(srcUri, type)会把照片选择器返回的临时URI用openSync加copyFileSync复制到filesDir/material_<时间戳>.<扩展名>,从而保证应用重启后仍可访问;扩展名从URI解析,解析失败时按类型回退为.jpg或.mp4。deleteMediaFile(path)仅允许删除沙箱内、即filesDir前缀的文件,避免误删外部文件。

查询接口方面,getAll()按素材日期降序返回副本,getById(id)做精确查找,save()和remove()维护内存数组并立即持久化,删除记录时联动删除媒体文件。这里的架构取舍比较明确:轻量应用用整表JSON写入Preferences实现简单,媒体文件单独复制到沙箱则解决了临时URI不可长期依赖的问题;如果后续数据量明显增长,才需要再评估拆分存储或分页策略,原文尚未展开。

四、首页列表与版本号刷新
首页Index.ets使用List加ForEach渲染素材卡片,卡片由MaterialCard Builder生成,包含Thumbnail、标题、类型标签、两行内容摘要和素材日期。Thumbnail按类型区分:图片显示沙箱图片,视频显示深色“播放”占位,文字显示紫色“文”字占位。点击卡片时设置activeItemId并跳转详情。搜索使用TextInput的onChange实时过滤标题和内容,无记录或无搜索结果时显示空态提示。

数据刷新采用@StorageLink('materialRevision')配合@Watch('onDataChanged')监听全局版本号。任何保存或删除操作递增该版本号后,首页自动重新加载列表;ForEach的key使用id加updatedAt,保证内容更新后重建卡片。这个做法把存储变更和页面刷新解耦,适合多页面共享同一份素材数据的场景。

五、详情页、编辑页与媒体选择
详情页Detail.ets以NavDestination承载,由首页Navigation管理,返回时调用pathStack.pop()。进入页面后从store.activeItemId读取目标素材id并加载,同时监听materialRevision,编辑返回后自动刷新。页面展示类型标签与素材日期、大标题、媒体区、记录内容卡片以及创建和最近更新时间卡片;媒体区按类型使用Image预览、Video组件播放或文字占位卡片。编辑或重新编辑时设置activeItemId后调用pushPathByName('Edit', id)。删除时通过this.getUIContext().showAlertDialog(...)弹出确认框,确认后调用store.remove()、递增版本号、toast提示并返回列表;素材被删除后再次进入会显示“素材不存在或已被删除”的兜底界面。

编辑页Edit.ets同样为NavDestination组件,同时承担新建和编辑两种模式,由activeItemId是否为空区分,标题栏文案随之变化。表单包括标题TextInput、文字/图片/视频胶囊按钮TypeChip、媒体区MediaSection、素材日期行和内容TextArea。changeType()切换类型时,如果存在未保存的新复制媒体文件会先删除,避免残留,并清空当前媒体路径。pickMedia()使用@kit.MediaLibraryKit的photoAccessHelper.PhotoViewPicker,按类型设置MIMEType为IMAGE_TYPE或VIDEO_TYPE,选中后经store.copyMedia()复制进沙箱;重复选择时清理上一份未保存的拷贝,取消选择和复制失败均有提示。日期选择通过this.getUIContext().showDatePickerDialog(...)完成,确认后写入时间戳。

保存时,save()会校验标题非空,图片或视频类型必须有媒体文件;随后组装MaterialItem,保留原createdAt并刷新updatedAt,写入存储;如果更换了媒体文件或类型改为文字,则删除旧文件;最后递增版本号、提示并返回上一页。

六、排障与验证关注点
从原文实现可以推导出几个容易出问题的点:照片选择器返回的是临时URI,必须复制到沙箱后再持久化;扩展名解析失败要有按类型回退的策略;删除媒体文件前要校验filesDir前缀;重复选择媒体或切换类型时要清理未保存的拷贝,避免沙箱残留;保存前要校验必填项;删除记录后详情页要有兜底界面;页面刷新依赖materialRevision版本号,ForEach的key包含updatedAt才能保证更新生效。测试验证可以覆盖新建文字、图片、视频素材,编辑时更换媒体,类型切换为文字,删除记录并联动删除媒体,应用重启后媒体仍可访问,以及搜索和空态展示等路径。

整体看,这个素材记录应用的价值不在于功能复杂,而在于把ArkTS页面组织、Preferences轻量持久化、沙箱媒体文件管理和版本号刷新串成了一条可落地链路。对于类似个人记录、草稿收集、轻量内容管理类HarmonyOS应用,这套实现思路具备直接参考价值。
回复

使用道具 举报

发表于 半小时前 | 显示全部楼层

Re: ArkTS素材记录应用:Preferences与沙箱文件存储实践

看完收获挺大,这个实践把几个容易踩坑的地方都点到了。整表 JSON 塞进 Preferences 对轻量工具确实够用,写起来简单,读出来直接就是一个数组,省掉了建表和字段映射的麻烦。不过我会有点担心数据量上来之后单键的写入和解析成本,比如素材攒到几百上千条、内容又比较长的时候,每次 save 都要全量序列化再 flush,操作感可能会变钝。如果后续真遇到这个瓶颈,也许可以按日期分键,或者干脆换到关系型存储,列表页只取摘要字段。 版本号刷新的做法也很实用,@StorageLink 加 @Watch 把存储变更和页面刷新解耦,多页面共享同一份数据时比手动回调省心。这里想请教一下,materialRevision 递增之后,Detail 页和 Index 页同时监听,会不会出现重复加载或者监听没来得及解绑的情况?我一般会在 aboutToDisappear 里做清理,但 @Watch 这种写法有时候容易漏。 另外几个细节我觉得挺关键的:照片选择器的临时 URI 必须复制到沙箱,扩展名解析失败按类型回退,删除前校验 filesDir 前缀,还有切换类型时先清掉未保存的新拷贝。这几条基本是这个场景的必备防线,尤其最后一条,不处理的话沙箱里很容易堆一堆没人引用的孤儿文件。想问下楼主有没有考虑过对复制进来的图片做一次压缩或者尺寸限制?拍摄原图直接进沙箱,长期用下来占用可能会比预期大不少。
回复 支持 反对

使用道具 举报

发表于 半小时前 | 显示全部楼层

Re: ArkTS素材记录应用:Preferences与沙箱文件存储实践

感谢分享,这篇把轻量素材记录应用的实现闭环讲得挺清楚。我比较认同几个取舍:结构化数据整表 JSON 写入 Preferences,媒体文件复制到 filesDir 沙箱,既降低了早期实现复杂度,又解决了照片选择器临时 URI 不能长期依赖的问题;用 materialRevision 配合 @Watch 做跨页刷新,也比各页面手动维护状态省心。几个细节确实值得注意:选择器 URI 要先复制再持久化,扩展名解析失败要按类型回退,删除媒体前校验 filesDir 前缀,以及切换类型时清理未保存的拷贝,这些都是实际容易踩坑的地方。我比较想了解的是,整表 JSON 在记录数量增长后读取和 flush 的耗时大概是什么量级;另外 materialRevision 递增后首页全量重载,如果素材较多,是否考虑过只更新受影响项。整体看,作为轻量工具的实现样本很有参考价值。
回复 支持 反对

使用道具 举报

发表于 半小时前 | 显示全部楼层

Re: ArkTS素材记录应用:Preferences与沙箱文件存储实践

看完挺有收获的,这个案例把轻量工具从数据契约到页面闭环走了一遍,对刚接触ArkTS的人比较友好。几个点我比较有共鸣。 一是临时URI必须复制进沙箱这个坑。我之前做图片记录时也踩过,直接存了picker返回的URI,当时能显示,重启后全变空白,排查了半天才发现是临时授权的问题。后来改成copyFileSync到filesDir才稳定下来。你这里解析扩展名失败按类型回退的处理挺细的,我没做这层,遇到一些奇怪的文件名时偶尔会丢后缀。 二是用@StorageLink加@Watch监听版本号来刷新,比在onShow里手动reload干净,详情页编辑返回后能自动更新这点很省心。不过我有点疑问:保存成功后递增版本号,到页面返回之间有个时间差,如果这时别的页面也监听了materialRevision,会不会触发一次多余的加载?还是说轻量场景下这种开销可以忽略,实际没刻意处理时序? 三是整表JSON写Preferences,条数少的时候确实简单痛快。但我印象里Preferences对单个value的大小有建议上限,如果素材正文写长了、条数也上去了,可能得留意一下。有没有大致试过多少条或者多长内容开始出现卡顿? 另外楼主最后一段好像没写完,filesDir前缀校验那段能再展开讲讲吗?我现在就是简单用startsWith判断,但会担心filesDir/../这种路径绕过去,不知道要不要再做个规范化处理,
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-8 01:49 , Processed in 0.025663 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部