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

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐