You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android WebView切割大Blob文件触发DOMException错误求助

在Ionic+Capacitor Android WebView中处理大文件时的DOMException调试方案

问题描述

在基于Ionic和Capacitor的Android WebView环境中处理0.97GB文件时,执行chunkify函数遇到DOMException错误。函数执行到第7次循环(实际完成6次)时,在最后一条console.log后触发以下错误:

Uncaught (in promise) DOMException: The requested file could not be read, typically due to permission problems that have occurred after a reference to a file was acquired.

原函数代码:

const chunkify = async (file: Blob) => {
  const totalSize = file.size;
  const chunkSize = 1024 * 1024 * 100; // 100MB

  const chunks = [] as Uint8Array[];
  const amountOfChunks = Math.ceil(totalSize / chunkSize);

  console.log("Total blob size: ", totalSize);
  for (let index = 0; index < amountOfChunks; index++) {
    const start = index * chunkSize;
    const end = (index + 1) * chunkSize;

    console.log("Start point: ", start);
    console.log("End point: ", end);
    const chunk = await file.slice(start, end).arrayBuffer();
    chunks.push(new Uint8Array(chunk));
  }

  return chunks;
};

注:已尝试将const end = (index + 1) * chunkSize;替换为const end = Math.min(totalSize, (index + 1) * chunkSize);,但未解决问题。

调试方案

1. 解决内存堆积问题

Android WebView单进程内存限制严格,连续加载100MB chunk并存储到数组会快速耗尽内存,导致文件读取失败。

  • 优化思路:不将所有chunk存储在内存中,改为流式处理(如处理完立即上传),并手动释放临时资源。
  • 修改示例:
const chunkify = async (file: Blob, processChunk: (chunk: Uint8Array) => Promise<void>) => {
  const totalSize = file.size;
  const chunkSize = 1024 * 1024 * 100; // 100MB
  const amountOfChunks = Math.ceil(totalSize / chunkSize);

  console.log("Total blob size: ", totalSize);
  for (let index = 0; index < amountOfChunks; index++) {
    const start = index * chunkSize;
    const end = Math.min(totalSize, (index + 1) * chunkSize);

    console.log("Start point: ", start);
    console.log("End point: ", end);
    const chunkBuffer = await file.slice(start, end).arrayBuffer();
    const uint8Chunk = new Uint8Array(chunkBuffer);
    
    // 立即处理chunk(如上传)
    await processChunk(uint8Chunk);
    
    // 手动清空数据,帮助GC回收内存
    uint8Chunk.fill(0);
  }
};

2. 修复文件权限问题

Android中文件Uri权限可能在后台切换、页面操作后失效,或WebView文件访问权限不足。

  • 优先使用Capacitor的Filesystem插件获取文件,而非原生input元素,插件能提供更稳定的持久访问权限。
  • 若使用input获取文件,需在AndroidManifest.xml中添加对应权限:
<!-- Android 12及以下 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<!-- Android 13+,根据文件类型选择 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

3. 减小切片大小

部分Android WebView版本对大Blob切片支持不佳,可尝试减小chunk size避免触发底层错误。

  • 将chunkSize修改为1024 * 1024 * 50(50MB),测试是否能完成所有切片操作。

4. 避免事件循环阻塞

连续的await调用可能阻塞WebView事件循环,影响文件读取线程的正常运行。

  • 在循环中加入微小延迟,给WebView留出处理其他任务的时间:
const chunkify = async (file: Blob) => {
  const totalSize = file.size;
  const chunkSize = 1024 * 1024 * 100;
  const amountOfChunks = Math.ceil(totalSize / chunkSize);
  const chunks = [] as Uint8Array[];

  console.log("Total blob size: ", totalSize);
  for (let index = 0; index < amountOfChunks; index++) {
    // 加入10ms延迟,避免阻塞事件循环
    await new Promise(resolve => setTimeout(resolve, 10));
    
    const start = index * chunkSize;
    const end = Math.min(totalSize, (index + 1) * chunkSize);

    console.log("Start point: ", start);
    console.log("End point: ", end);
    const chunk = await file.slice(start, end).arrayBuffer();
    chunks.push(new Uint8Array(chunk));
  }

  return chunks;
};

内容的提问来源于stack exchange,提问作者Oyedeji

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:00:18