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

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);

验证要点

  1. 确保JS代码中完全移除对responseText的访问,无论XHR的responseType设置为何种类型。
  2. 大文件下载优先采用Android端直接下载方案,彻底规避WebView内存限制。
  3. 使用真实100MB+文件测试,确认下载流程无报错且文件完整。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 12:26:19