ImageKit×ArkUI:应用上架截图像素规格与文件签名预检【鸿蒙心迹】
上架素材被退回修改时,最让人头疼的往往不是截图内容本身,而是很细的交接问题:文件名写着 PNG,实际可能从另一套工具导出成不同格式;所有截图看起来一样高,偏偏有一张是 750×1334。开发负责人接到这些图片后,凭肉眼一张张检查,不仅慢,还会在迭代版本里重复做相同工作。这次把问题收窄,只做一个本地“素材预检”小页面。Demo 叫 AssetGate,使用 ImageKit 读取图片基本信息,ArkUI 展示检查明细。它不负责替代应用市场审核,不判断图片内容是否合规,不使用未核实的“自动审核通过率”,只是帮助开发团队在提交之前发现文件签名、尺寸与数量这几类基础问题。适用范围明确,工具就不会被误认为万能审核器。
## 一、先核对规则,再决定检查什么AppGallery Connect 官方视觉素材规格中,对 HarmonyOS 手机和平板的应用截图列出方向、比例、像素分辨率、数量与格式等要求。依据当前可查到的官方页面,竖版截图可按 9:16、1080×1920 核对,PNG/JPG/JPEG 单张最大 5MB,每个方向通常要求 3~5 张。提交入口、地区、终端类别以及后续政策都可能带来差异,最终仍要以真实提交页面提示为准。这里选取其中很窄的一部分作为演示规则,不能宣称覆盖全部平台场景。AssetGate 第一版只处理预置 rawfile 目录中的竖版 PNG 图片。用户外部选取、JPEG 解码、自动拼接长图、平板适配截图和审核内容判定暂不实现。与其把所有功能放在一篇文章里堆 API,不如先把可以验证的四张样例贯通:P01.png、P02.png、P03.png 为 1080×1920,P04.png 为 750×1334。四份文件是方案里的模拟数据,不对应真实已上传的用户素材。演示报表预期显示“总项数 4、通过 3、异常 1”。前三张符合这套尺寸规则,第四张由于像素尺寸不符而列为异常。虽然合格的竖图已经有 3 张,整个检查任务仍标为“待修正”,因为清单里保留了一份有问题的文件;操作员应确认是否删除或替换 P04,而不是看到 3/4 通过就直接进入正式上传。## 二、从字节入手识别假 PNG扩展名不能说明真实文件格式。最容易演示也最容易测试的一关,是检查 PNG 文件头。标准 PNG 签名由八个确定字节组成,这段代码解决的是“文件名正确但字节开头就不对”的问题。签名通过不代表文件完整,遇到内容截断或关键数据块损坏仍要让后续解码器发现。typescript// entry/src/main/ets/checks/PngHeader.etsconst PNG_SIGNATURE: number[] = [137, 80, 78, 71, 13, 10, 26, 10];export function hasPngSignature(bytes: Uint8Array): boolean { if (bytes.byteLength < PNG_SIGNATURE.length) return false; for (let i = 0; i < PNG_SIGNATURE.length; i++) { if (bytes[i] !== PNG_SIGNATURE[i]) return false; } return true;}这里不把“长度大于八”当成合法判断,也不尝试用 UTF-8 文本比较所有字节。对于只有八字节签名、没有实际图片数据的文件,本函数会给出初步通过,后续 ImageKit 读取图像信息时应当失败。因此日志里应该使用精确的“PNG 签名匹配”而不是“文件已验证合法”。实际项目还需要把读取失败、解码失败、签名不符区分成不同问题,便于设计人员回查导出环节。## 三、只取尺寸,不顺手解码整张位图预检页面需要的是宽度、高度和字节数,并不需要生成一个 PixelMap 才能比较像素。官方文档展示了通过资源管理器获取 rawfile 字节、用 ImageKit 创建 ImageSource 的方式。下面的封装先做签名检查,再调用 getImageInfo,成功时返回最小的元数据对象,失败时把异常交给页面处理。typescript// entry/src/main/ets/checks/InspectAsset.etsimport { image } from '@kit.ImageKit';import { hasPngSignature } from './PngHeader';export interface AssetMeta { name: string; width: number; height: number; bytes: number; }export async function inspectAsset(context: Context, name: string): Promise<AssetMeta> { const data: Uint8Array = await context.resourceManager.getRawFileContent(name); if (!hasPngSignature(data)) throw new Error('PNG_SIGNATURE_MISMATCH'); const buffer: ArrayBuffer = data.buffer.slice( data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer; const source: image.ImageSource = image.createImageSource(buffer); try { const info: image.ImageInfo = await source.getImageInfo(); return { name, width: info.size.width, height: info.size.height, bytes: data.byteLength }; } finally { await source.release(); }}代码保留了 finally:不论读取尺寸是否成功,都尝试释放 ImageSource,避免扫描几十张文件后让临时对象在内存里积累。data.byteOffset 与 byteLength 用于截取实际有效的字节范围,以免把底层 ArrayBuffer 中与当前视图无关的区域一起传入。rawfile 文件名应来自应用内固定可信清单,不应直接与外部任意路径拼接。实际接入时要核对目标 SDK 的 ImageSource.release 异步签名,并在真实工程里处理释放失败。本文没有指定一个未经验证的 SDK 版本,也没有声称这段片段已经在真机上完成批量检测。rawfile 是跟随应用打包的资源,适合作为稳定的教学样例;如果要检查用户即时选择的外部文件,还必须增加官方选择器、URI 授权、文件导入与生命周期管理,不能仅把参数换成手机路径就宣称实现了。## 四、单张校验与整组状态不要混为一谈应用市场的截图要求还涉及数量。一张图片尺寸正确,只说明这一张符合所选规则,不代表整组已经完整。因此让规则成为独立函数,不把“通过”写在 UI 里。下面示例只关心竖版 PNG,其他方向先明确显示“不在本轮规则中”。5MB 在本地先按 5 * 1024 * 1024 字节处理,这个换算是程序采用的预检阈值,不是断言官方所有入口都把 MB 解释为 MiB;正式提交前仍应核对页面限制。typescript// entry/src/main/ets/checks/PortraitRule.etsimport { AssetMeta } from './InspectAsset';export const MAX_BYTES: number = 5 * 1024 * 1024;export function validatePortrait(meta: AssetMeta): string | undefined { if (meta.width !== 1080 || meta.height !== 1920) return 'PIXEL_SIZE_MISMATCH'; if (meta.bytes > MAX_BYTES) return 'FILE_TOO_LARGE'; return undefined;}export function isReady(validCount: number, failedCount: number): boolean { return validCount >= 3 && validCount <= 5 && failedCount === 0;}这几个返回值是本项目的业务标识,不是华为平台的系统错误码。针对 P04,应显示“尺寸不符,实际 750×1334,预期 1080×1920”;针对没有完整 PNG 文件头的样例,应显示“文件签名不符”,而不是“尺寸不符”。isReady 保留了 failedCount===0 的条件,是为了阻止被遗漏的异常项藏在一组通过项后面。若未来产品决定允许多余文件自动剔除,需要重新定义清单生成方式和操作提示,而非简单删除这个条件。
## 五、一屏内把问题交代清楚手机演示图的首页名称为“素材预检”,顶部显示 AssetGate · 竖版 PNG 基础校验,报告汇总为 4 项、3 通过、1 异常。四张图片按 P01 至 P04 顺序排列,异常项突出实际尺寸 750×1334,整组状态是“待修正”。底部按钮文字为“重新检查”,绝不写成“提交审核”。这些字段与 DevEco 演示图里 scan=4 pass=3 fail=1 的日志一致,便于读者核对。
调试需要覆盖比正常样例更麻烦的情况。其一,把 PNG 扩展名对应的字节换成 JPEG,检查是否停在签名阶段。其二,只保留签名不提供完整图像,检查 ImageKit 错误能否被显示而不是导致页面空白。其三,提供 750×1334 的有效 PNG,确认报告记录的是尺寸异常。其四,构造大于上限的有效文件,确认字节数判断发生在正确位置。这些是应当执行的测试计划,不是已经收集到的运行结果。重复点击“重新检查”同样需要防护。在静态样例里,可以用 isChecking 阻止并发重复扫描;将来若要支持从相册连续选择不同批次,还应分配批次标识,只接纳最新批次回填结果。页面退出时要停止对已经失效页面的状态写入,异步调用结束后及时清理引用。这一段是工程扩展建议,而非宣称上面的三段代码已经实现了完整的队列系统。## 六、预检的终点不是“官方审核通过”签名和分辨率正确,依然不能判断图片是否展示了真实应用、是否存在不合规宣传、是否符合区域性政策。素材检查也无法替代包体检查、隐私说明、应用内容审核等流程。页面应把“本地规则通过”和“平台审查通过”写成两个不同概念,尤其不能在设计评审会上拿演示界面充当官方结论。这套实现的好处在于让不同团队对同一个问题使用同一种说法:先确认文件本身可识别,再比较像素和大小,最后检查组内数量,随后由人决定提交与审核事项。即使后续增加 JPEG/WebP、横版截图、多设备规则,也能沿用这套分层结构;每扩一个场景就要同步补测试样本和规则来源。本文图片、日志和 P01~P04 均为演示数据,尚未通过真实 DevEco 编译及应用市场上传检验。官方依据:AppGallery Connect 视觉素材规格、ImageSource 创建与 rawfile 读取、rawfile 文件管理说明。以上资料用于确认能力与业务规则,真正接入仍需按当前提交页面和目标 SDK 复核。
更多推荐



所有评论(0)