Android WebView下载100MB+Blob文件失败排查及解决方案请求
问题排查与解决方案
错误核心原因
报错信息已明确指出:当XMLHttpRequest的responseType设置为'blob'时,无法读取responseText属性,这是W3C XMLHttpRequest规范的强制要求。小文件下载成功属于偶然兼容情况,大文件下载时WebView的严格校验触发了规范限制,直接导致下载中断。
具体修复方案
1. 修正JavaScript端XHR处理逻辑
确保在responseType='blob'时,仅通过response属性获取Blob对象,绝对禁止访问responseText。
示例修正后的JS下载逻辑:
function downloadLargeFile(url) { const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.responseType = 'blob'; // 明确设置为blob类型 xhr.onload = function() { if (xhr.status === 200) { // 直接读取response获取Blob对象,不要使用responseText const blob = xhr.response; // 调用Android端接口传递Blob window.AndroidInterface.handleBlobDownload(blob); } }; xhr.onerror = function() { console.error('下载请求失败'); }; xhr.send(); }
2. 优化Android端Blob处理方案
WebView直接传递大Blob对象易触发内存瓶颈,推荐优先采用Android端直接发起下载的方案,避免WebView中转数据:
方案A:Android端直接下载(推荐)
让JavaScript将文件URL传递给Android,通过系统DownloadManager或OkHttp等库直接下载,彻底规避WebView的内存限制:
// JavaScriptInterface中的方法 @JavascriptInterface public void startFileDownload(String fileUrl, String fileName) { DownloadManager.Request request = new DownloadManager.Request(Uri.parse(fileUrl)); request.setTitle(fileName); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName); DownloadManager downloadManager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); if (downloadManager != null) { downloadManager.enqueue(request); } }
JS端调用改为:
window.AndroidInterface.startFileDownload('https://example.com/large-file.zip', 'large-file.zip');
方案B:分块传递Blob(仅适用于必须通过WebView中转的场景)
若必须通过WebView传递Blob,可将Blob分块读取为ArrayBuffer,逐块传递给Android后拼接:
JS端分块逻辑:
function sendBlobChunks(blob) { const chunkSize = 1024 * 1024; // 按1MB分块 let offset = 0; function readNextChunk() { const end = Math.min(offset + chunkSize, blob.size); const chunk = blob.slice(offset, end); const reader = new FileReader(); reader.onload = function() { const base64Chunk = btoa(String.fromCharCode(...new Uint8Array(reader.result))); // 传递分块数据,标记是否为首尾块 window.AndroidInterface.receiveBlobChunk(base64Chunk, offset === 0, end === blob.size); offset = end; if (offset < blob.size) { readNextChunk(); } }; reader.readAsArrayBuffer(chunk); } readNextChunk(); }
Android端接收分块并拼接:
private FileOutputStream outputStream; private File targetFile; @JavascriptInterface public void receiveBlobChunk(String base64Chunk, boolean isFirstChunk, boolean isLastChunk) { try { if (isFirstChunk) { targetFile = new File(getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "large-file.bin"); outputStream = new FileOutputStream(targetFile); } byte[] chunkBytes = Base64.decode(base64Chunk, Base64.DEFAULT); outputStream.write(chunkBytes); if (isLastChunk) { outputStream.close(); // 下载完成后的后续逻辑 } } catch (IOException e) { e.printStackTrace(); } }
3. WebView配置优化
开启必要配置,避免大文件下载时的内存或权限问题:
- 启用DOM存储:
webSettings.setDomStorageEnabled(true); - 设置足够的缓存大小:
webSettings.setAppCacheMaxSize(1024 * 1024 * 200); // 200MB - 允许文件访问:
webSettings.setAllowFileAccess(true);
验证要点
- 确保JS代码中完全移除对
responseText的访问,无论XHR的responseType设置为何种类型。 - 大文件下载优先采用Android端直接下载方案,彻底规避WebView内存限制。
- 使用真实100MB+文件测试,确认下载流程无报错且文件完整。
内容的提问来源于stack exchange,提问作者Mimo Saha
相关产品推荐
相关产品推荐

