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
相关产品推荐
相关产品推荐

