HarmonyOS应用实战-启示散页-46-文章素材别混进应用包:把 CSDN 图片和运行资源分开管理
发布前做包体复查时,最容易被忽略的一类问题不是“图片太大”,而是“这张图片本来就不该进包”。答案之书这类项目经常同时维护应用源码、商店截图、CSDN 文章封面、流程图和结构图。目录一乱,文章用的 PNG 很容易被拷进entry/src/main/resources/base/media,最后 HAP 变大,资源来源说不清,发布复盘也不知道该删哪一个文件。
这篇文章只解决一个具体问题:CSDN 文章素材如何和 HarmonyOS 运行资源彻底分开。读完后你应该能拿到四个结果:
- 判断一张图片到底服务文章、商店发布,还是应用运行时。
- 用资源账本解释包体里每个新增文件的 owner。
- 用脚本在提交前拦住文章图片误入
resources或rawfile。 - 把检查结果写进发布清单,而不是只说“构建通
先还原一次真实事故链
假设发布前发现 HAP 比上一个版本多了几百 KB。第一反应通常是压缩图片、删日志、查依赖。但继续往下看,发现新增文件里有the_book_of_answers_46_cover.png、the_book_of_answers_46_flow.png这类文章图片。它们不是页面图标,也不是启动页素材,只是为了 CSDN 文章排版准备的说明图。
这时问题已经不是压缩率,而是资源归属失控:
| 现象 | 真实原因 | 后果 |
|---|---|---|
| HAP 包体变大 | 文章 PNG 被复制进运行资源 | 发布包携带无用素材 |
| 删除图片怕影响页面 | 没有 owner 记录 | 不敢清理资源目录 |
| 文章换图后应用也变化 | 共用同一张文件 | 内容发布影响应用运行 |
| 审核截图和运行资源混在一起 | 发布素材没有独立目录 | 复盘时找不到证据边界 |
已核对的当前目录边界是:本系列文章图片位于media/the_book_of_answers/46/,应用运行资源应由entry、libraryHAR、libraryHSP各自模块管理。本文不声称已经改过应用源码,只给出可落地的检查与隔离方法。
三类图片先分清 owner
资源治理的第一步不是写脚本,而是给每类图片命名。只看后缀和尺寸不够,文章封面、商店截图、应用图标都可能是 PNG;真正能区分它们的是“谁消费它”。
| 类型 | 推荐目录 | 消费方 | 是否进入 HAP |
|---|---|---|---|
| CSDN 文章图 | media/the_book_of_answers/<no>/ | Markdown / CSDN 编辑器 | 否 |
| 商店截图 | release/screenshots/或发布清单目录 | AGC 提交材料 | 否 |
| 应用运行图标 | entry/src/main/resources/base/media/ | ArkUI$r('app.media.xxx') | 是 |
| 默认题库 rawfile | entry/src/main/resources/rawfile/ | SeedLoader/$rawfile(...) | 是 |
| HSP 自带组件资源 | libraryHSP/src/main/resources/ | HSP 页面或组件 | 是 |
这个表的作用很直接:如果一张图只是 Markdown 文章里的流程图引用,它就不应该出现在entry/src/main/resources。如果一张图被 ArkUI 页面通过$r('app.media.xxx')引用,它就不应该放在文章素材目录里。
用资源账本记录新增文件
轻量项目不一定需要复杂资产平台,但至少要有一份可读的资源账本。账本不参与运行时逻辑,只服务发布复查:谁加了文件、文件服务什么、是否打包、能否删除。
typeAssetOwner='article'|'agcScreenshot'|'entryRuntime'|'harRuntime'|'hspRuntime'|'rawSeed';interfaceResourceLedgerItem{path:string;owner:AssetOwner;purpose:string;packagedInHap:boolean;referencedBy:string;removableAfterPublish:boolean;}这段模型的重点不是字段多,而是owner和packagedInHap。文章图的packagedInHap必须是false;运行资源的referencedBy必须能指向具体页面、组件或启动服务。只要这两项说不清,就不要把文件留进提交。
一条合格记录可以长这样:
{"path":"media/the_book_of_answers/46/the_book_of_answers_46_flow.png","owner":"article","purpose":"第46篇CSDN流程图","packagedInHap":false,"referencedBy":"HarmonyOS应用实战-启示散页-46.md","removableAfterPublish":false}如果同样的文件被复制进entry/src/main/resources/base/media/,账本就应该报错,而不是让它混进发布包。
HarmonyOS 运行资源只认模块目录
HarmonyOS 工程里,运行资源有明确归属:element放字符串、颜色和尺寸,media放图标和图片,profile放路由或配置,rawfile放保留原始格式的资源。文章图片不属于其中任何一类。
entry/src/main/resources/ base/ element/ string.json color.json media/ app_icon.png home_empty_deck.png profile/ main_pages.json router_map.json rawfile/ seed/default_deck.json media/the_book_of_answers/46/ the_book_of_answers_46_cover.png the_book_of_answers_46_flow.png the_book_of_answers_46_structure.png这里的分界线很硬:home_empty_deck.png如果被首页Image($r('app.media.home_empty_deck'))使用,就应该在运行资源目录;the_book_of_answers_46_flow.png只被 Markdown 引用,就应该留在文章目录。不要因为两者都是 PNG 就放到同一个地方。
页面引用运行资源时要能说清用途
真正需要进入应用包的图片,也不能只靠文件名存在。页面应该通过$r引用模块内资源,并且文件名能说明业务用途。
@Componentstruct EmptyDeckPanel{build(){Column({space:12}){Image($r('app.media.home_empty_deck')).width(120).height(120)Text($r('app.string.home_empty_deck_title'))Text($r('app.string.home_empty_deck_desc'))}}}这段代码的 owner 很清楚:home_empty_deck服务首页空题库状态,属于运行资源,可以进入 HAP。与它相对,CSDN 的文章流程图没有任何 ArkUI 引用,不应该为了“方便查看”放进base/media。
资源配置也要保持同样边界:
{ "module": { "name": "entry", "type": "entry", "srcEntry": "./ets/Application/AbilityStage.ets", "description": "$string:module_desc", "mainElement": "EntryAbility", "pages": "$profile:main_pages" } }module.json5只描述模块运行入口和资源配置,不应该引用文章素材目录。文章和发布说明可以描述应用,但不能变成应用的构建输入。
提交前先跑两组扫描
第一个扫描从文章出发:确认 Markdown 引用的都是文章目录下的 PNG,而且文件存在。
$article="C:\Users\ADcaing\Desktop\csdn\HarmonyOS应用实战-启示散页-46-文章素材别混进应用包:把 CSDN 图片和运行资源分开管理.md"$root=Split-Path-Parent$article$refs=Select-String-LiteralPath$article-Pattern'!\[[^\]]+\]\(([^)]+\.png)\)'-AllMatchesforeach($matchin$refs.Matches){$relative=$match.Groups[1].Value$full=Join-Path$root$relativeif(-not(Test-Path-LiteralPath$full)){throw"文章图片不存在:$relative"}if($relative-notlike"media/the_book_of_answers/46/*.png"){throw"第46篇图片路径越界:$relative"}}第二个扫描从应用工程出发:确认运行资源目录里没有文章素材特征词。这里的目标不是证明源码一定正确,而是拦住最常见的误复制。
$project="D:\ProgramData\huawei\lesson\The_Book_of_Answers"$resourceRoots= @("$project\entry\src\main\resources","$project\libraryHAR\src\main\resources","$project\libraryHSP\src\main\resources")foreach($rootin$resourceRoots){if(Test-Path-LiteralPath$root){$hit=Get-ChildItem-LiteralPath$root-Recurse-File|Where-Object{$_.Name-match'the_book_of_answers|csdn|article|cover_flow|structure'}if($hit){$hit|ForEach-Object{Write-Output$_.FullName}throw"文章素材疑似进入应用运行资源"}}}这两个扫描方向不同:一个保证文章能正常发布,一个保证应用包不带文章素材。只做其中一个都不完整。
包体复查要看“新增资源来源”
如果构建后发现包体变大,排查顺序建议从新增资源来源开始,而不是直接压缩。压缩只能降低体积,不能解决不该打包的问题。
$before=Get-Content".\release\resource-list-before.txt"$after=Get-Content".\release\resource-list-after.txt"$added=Compare-Object$before$after|Where-Object{$_.SideIndicator-eq"=>"}$added|ForEach-Object{$path=$_.InputObjectif($path-match'the_book_of_answers|csdn|article'){throw"发现文章素材进入包体清单:$path"}Write-Output"新增资源待确认 owner:$path"}实际项目里可以从构建产物、资源清单或手工导出的文件列表中拿before/after。重点是每个新增资源都要回答两个问题:它被哪个运行页面使用?如果删除它,应用哪条路径会失败?
反例:把文章目录当成备用素材库
最危险的写法不是偶尔复制一张图,而是团队默认把media当成素材库。比如首页缺一个空态图,就从 CSDN 文章里挑一张流程图放进resources/base/media;发布截图缺一张说明图,又从运行资源里复制出去改尺寸。几轮之后,任何图片都说不清来源。
反例通常有三个症状:
| 症状 | 为什么危险 | 正确处理 |
|---|---|---|
文件名只有cover.png | 无法判断属于文章还是页面 | 用home_empty_deck.png、article_46_flow.png区分用途 |
| 文章图和运行图共用路径 | 内容发布会影响应用包 | 文章目录和资源目录物理隔离 |
| 删除资源只靠人工记忆 | 发布前不敢清理 | 资源账本记录 owner 和引用点 |
遇到这种情况,不要先批量重命名。先按 owner 分类,把不该进入 HAP 的文件移出运行资源,再补账本和扫描脚本。否则只是把混乱资源换了一批新名字。
验证清单
第 46 篇文章发布前,我建议按这张清单复查:
- Markdown 中 3 张图片都指向
media/the_book_of_answers/46/。 - 3 张图片都是本地 PNG,且文件真实存在。
entry/src/main/resources、libraryHAR/src/main/resources、libraryHSP/src/main/resources中没有the_book_of_answers、csdn、article等文章素材命名。- 应用运行资源里的新增图片能找到 ArkUI
$r('app.media.xxx')或启动服务引用。 - 发布清单记录文章素材目录与应用资源目录相互独立。
- 包体变大时先查新增资源 owner,再考虑压缩。
这里没有做真机运行验证,也没有上传 CSDN。本文给出的验证是本地文件、Markdown 引用和工程资源边界检查;如果要证明发布结果,还需要在 CSDN 编辑器里完成图片上传和正文预览。
常见问题与定位
| 现象 | 先看哪里 | 处理 |
|---|---|---|
| HAP 体积突然变大 | 新增资源清单 | 找出是否有文章 PNG 进入 resources |
| 文章图片发布后丢失 | Markdown 图片路径 | 确认本地 PNG 存在,发布前上传到 CSDN 图床 |
| 删除某张图担心影响应用 | 是否有$r或$rawfile引用 | 没有运行引用且 owner 是 article,就不应在 HAP |
| 同名图片到处都有 | 文件命名规则 | 按业务用途重命名,不按临时来源命名 |
| rawfile 里出现说明图 | rawfile 消费方 | rawfile 只留 SeedLoader 等真实运行输入 |
排查时先从路径入手。文章素材路径应该能读出文章编号,运行资源路径应该能读出页面或组件用途。路径都说不清时,代码层面的引用通常也会跟着混乱。
小结
文章素材和运行资源的边界,本质上是发布责任边界。CSDN 图片服务内容表达,商店截图服务提交材料,resources和rawfile服务应用运行。把这三类资产混在一起,包体、审核和排障都会变得模糊。更稳的做法是:目录先隔离,账本写 owner,提交前双向扫描,包体复查只接受能解释来源的新增资源。