为什么使用fetch时await未等待APK下载完成就执行文件写入
问题根因
- 代码中使用的
mode: 'no-cors'从一开始就选错了模式。这个模式的设计场景是加载不需要读取内容的跨域静态资源(比如埋点请求、像素追踪),根本不适合下载需要读取内容存本地的APK。这种模式下fetch的Promise刚拿到响应头、根本等不及把11MB的文件全部传输完成就直接resolve,后续调用blob()拿到的是残缺的、未传完的内容,这就是你感知到“await没生效、传输中就开始保存”的核心原因。 saveApk方法中调用this.file.writeFile()时没有加await。写文件是异步IO操作,这个调用发出后流程会直接向下执行,既捕获不到写入错误,也会因为没有等待流程完成,出现提前触发后续逻辑的错觉。- 没有对下载完成的blob做有效性校验,11MB的APK一旦出现传输截断,会直接把不完整的内容写入本地,生成无法安装的损坏包。
- 原代码的catch块只打日志不向上抛错误,一旦下载请求异常,上层会拿到值为undefined的blob,后续流程根本感知不到异常,排查时很容易被误导。
修正后可运行代码
async saveApk(){ try { const downloadedBlob = await this.download(this.url); // 基础完整性校验,避免残缺内容写入本地 if (downloadedBlob.size < 10 * 1024 * 1024) { throw new Error('下载的APK内容不完整'); } const apkBlob = new Blob([downloadedBlob], {type:'application/vnd.android.package-archive'}); // 必须await写文件的Promise,等待IO完成 await this.file.writeFile(this.file.cacheDirectory, 'test.apk', apkBlob, { replace: true}); } catch (e) { console.error('APK下载保存失败', e); } } async download(url){ try { const request = new Request(url, { method: 'GET', // 移除no-cors模式,Ionic WebView的跨域限制可通过配置放开,无需靠no-cors妥协 credentials: 'include' }); const response = await fetch(request); if (!response.ok) { throw new Error(`下载请求失败,状态码:${response.status}`); } const data = await response.blob(); return data; } catch (err) { console.error('fetch请求异常', err); throw err; // 向上抛出错误,不要静默吞掉异常 } }
注意事项
- 如果WebView确实存在跨域拦截,不要硬靠no-cors模式绕过,可在Ionic的WebView配置中放开跨域访问限制,从根源保证fetch能拿到完整的二进制响应。
- 后续下载更大体积的文件时,可以用
response.body.getReader()做流式分块写入,不要等整个Blob在内存中生成完再落盘,避免WebView内存占用过高。 - 排查时不要靠“await没生效”的模糊感知判断问题,可在每个异步节点打印当前blob的实际大小,就能直观看到哪个环节提前返回了不完整的数据。
内容的提问来源于stack exchange,提问作者Daniel Corbalan
相关产品推荐
相关产品推荐

