三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

HarmonyOS 7 / API 26 ArkWeb 文件上传适配:内核差异、权限边界和失败兜底一次验清

HarmonyOS 7 / API 26 ArkWeb 文件上传适配:内核差异、权限边界和失败兜底一次验清

HarmonyOS 7 / API 26 里用 ArkWeb 承载 H5 页面时,文件上传是一个很容易被低估的适配点。H5 页面里一个普通的 `<input type="file">`,在桌面浏览器里基本不会出问题,但放进应用之后,就会遇到权限、文件类型、回调生命周期、页面恢复和上架审核这些边界。

我一般不会只问“能不能选文件”。更稳的检查方式是:能不能选到正确类型,取消选择有没有兜底,页面切后台后回调还在不在,上传失败后用户能不能重试,应用权限声明和实际触发时机是否一致。

问题先拆开

ArkWeb 文件上传至少有四个风险点:

  • H5 允许的文件类型和应用实际能提供的文件类型不一致;
  • 用户取消选择以后,页面没有收到明确结果;
  • 选择文件时应用进入后台,回来后回调对象已经失效;
  • 页面要求上传敏感文件,但应用权限声明和说明不清楚。

这些问题单独看都不复杂,放到一起就会变成“线上偶现上传失败”。所以我会先把上传流程当成一个独立能力来验,而不是等业务页面写完再补。

案例一:文件类型没有收口

H5 里常见写法是这样:

<input type="file" accept="image/*,.pdf" />

这段代码只是在 H5 侧表达意图,不等于应用侧一定正确处理了类型。比如页面只想要图片和 PDF,但应用侧没有做白名单,最后可能把视频、压缩包或者不支持的文件也传进来。

我会在应用侧先定义一个类型白名单:

type UploadScene = 'avatar' | 'feedback' | 'document' type UploadRule = { scene: UploadScene mimeTypes: string[] maxSizeMb: number } const uploadRules: UploadRule[] = [ { scene: 'avatar', mimeTypes: ['image/png', 'image/jpeg'], maxSizeMb: 5 }, { scene: 'feedback', mimeTypes: ['image/png', 'image/jpeg', 'video/mp4'], maxSizeMb: 30 }, { scene: 'document', mimeTypes: ['application/pdf'], maxSizeMb: 20 }, ]

然后统一检查:

class UploadRuleChecker { check(scene: UploadScene, file: { mimeType: string; sizeMb: number }): string[] { const rule = uploadRules.find(item => item.scene === scene) const errors: string[] = [] if (!rule) { return ['上传场景没有配置规则'] } if (!rule.mimeTypes.includes(file.mimeType)) { errors.push(`文件类型 ${file.mimeType} 不在白名单内`) } if (file.sizeMb > rule.maxSizeMb) { errors.push(`文件大小 ${file.sizeMb}MB 超过 ${rule.maxSizeMb}MB`) } return errors } }

这一步不是为了把逻辑写复杂,而是为了让 H5、ArkWeb 回调和应用上传策略对齐。文件类型不对,越早挡住越好。

案例二:取消选择和后台恢复没有兜底

另一个常见问题是用户点了上传入口,但是没有真正选文件。可能是用户取消,也可能是切后台后流程被打断。如果页面一直等回调,就会卡在“上传中”。

我会给每一次选择分配一个请求 id:

class WebFileSelectGuard { private activeRequestId = '' start(): string { this.activeRequestId = `${Date.now()}-${Math.random()}` return this.activeRequestId } isActive(id: string): boolean { return this.activeRequestId === id } cancel(id: string) { if (this.isActive(id)) { this.activeRequestId = '' } } }

回调处理时先判断请求是否还有效:

async handleFileResult(requestId: string, files: UploadFile[]) { if (!this.fileSelectGuard.isActive(requestId)) { return } if (files.length === 0) { this.showUploadState('cancelled') return } const errors = this.ruleChecker.check('feedback', files[0]) if (errors.length > 0) { this.showUploadError(errors.join('\n')) return } await this.upload(files[0]) }

这样用户取消、页面销毁、后台恢复后的旧回调,都不会继续改当前页面状态。

用脚本检查上传规则

下面这个脚本可以直接跑,用来检查上传配置有没有明显漏洞:

const rules = [ { scene: 'avatar', mimeTypes: ['image/png', 'image/jpeg'], maxSizeMb: 5 }, { scene: 'feedback', mimeTypes: ['image/png', 'image/jpeg', 'video/mp4'], maxSizeMb: 30 }, { scene: 'document', mimeTypes: ['application/pdf'], maxSizeMb: 20 }, ] const cases = [ { scene: 'avatar', file: { mimeType: 'image/png', sizeMb: 2 } }, { scene: 'avatar', file: { mimeType: 'video/mp4', sizeMb: 2 } }, { scene: 'document', file: { mimeType: 'application/pdf', sizeMb: 28 } }, ] function checkUploadCase(item) { const rule = rules.find(rule => rule.scene === item.scene) const errors = [] if (!rule) { errors.push('缺少场景规则') return { ...item, passed: false, errors } } if (!rule.mimeTypes.includes(item.file.mimeType)) { errors.push('文件类型不允许') } if (item.file.sizeMb > rule.maxSizeMb) { errors.push('文件大小超限') } return { ...item, passed: errors.length === 0, errors } } const result = cases.map(checkUploadCase) console.log(JSON.stringify({ total: result.length, failed: result.filter(item => !item.passed).length, result, }, null, 2))

输出里应该有两个失败用例:

{ "total": 3, "failed": 2 }

一个是头像场景不允许视频,一个是 PDF 文件超过大小限制。这个脚本能提前把配置问题拦住,不要等用户上传失败以后才发现。

几种兜底方式对比

方案好处问题适合场景
H5 自己处理 accept接入快应用侧不可控,边界不清楚简单内部页面
应用侧统一白名单类型和大小明确需要维护规则生产应用
场景化上传规则最清楚,便于审核和排查前期设计成本更高多页面、多文件类型上传

我更倾向第三种。上传不是一个按钮,而是一组规则:场景、类型、大小、取消、失败、重试、权限说明,都应该放到同一个模型里。

发布前检查清单

检查项合格标准
文件类型H5 accept 和应用白名单一致
文件大小每个场景都有明确上限
取消选择用户取消后页面不会一直 loading
后台恢复旧回调不会覆盖新状态
失败兜底上传失败能重试,错误提示清楚
审核说明权限触发时机和使用目的能解释清楚

后面怎么避免

我会把 ArkWeb 文件上传当成应用能力来做,而不是当成 H5 的一个输入框:

  • 每个上传入口先定义场景;
  • 每个场景配置文件类型和大小上限;
  • 每次选择文件都有 requestId;
  • 用户取消和页面销毁都要有明确状态;
  • 上传失败保留重试入口;
  • 上架前检查权限说明和实际触发是否一致。

这样处理以后,ArkWeb 上传问题会从“偶现不好复现”变成“有规则、有日志、有兜底”的普通问题。后面无论是图片、反馈附件还是文档上传,都可以复用同一套检查方式。

← 返回列表