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

Android WebView中DownloadManager下载的PDF无法打开问题求助

问题分析与解决方案

核心问题

你遇到的问题根源在于Blob URL的错误处理:WebView中的blob: URL是浏览器内存中临时存储的文件引用,仅在当前WebView会话内有效,无法直接通过系统DownloadManager请求该URL获取真实文件内容。你直接去掉blob:前缀后的URL是无效的,导致下载的文件内容为空或损坏,因此无法打开。

验证方式

先检查下载后的PDF文件大小:如果文件仅几KB甚至更小,说明下载的是无效内容,进一步确认是Blob URL处理错误导致的。

解决方案:将Blob转换为Base64后保存

通过JavaScript将Blob对象转换为Base64编码字符串,再传递到Android原生代码中解码并保存为文件,具体实现步骤如下:

1. 创建JavaScript交互接口

class PdfDownloadInterface(private val context: Context) {
    @JavascriptInterface
    fun downloadPdf(base64String: String, fileName: String) {
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED
        ) {
            // 此处补充存储权限申请逻辑
            return
        }

        try {
            val bytes = Base64.decode(base64String, Base64.DEFAULT)
            val file = File(
                Environment.getExternalPublicDirectory(Environment.DIRECTORY_DOWNLOADS),
                fileName
            )
            FileOutputStream(file).use { outputStream ->
                outputStream.write(bytes)
            }

            // 通知系统扫描文件,更新下载列表
            MediaScannerConnection.scanFile(
                context,
                arrayOf(file.absolutePath),
                arrayOf("application/pdf")
            ) { _, _ ->
                Toast.makeText(context, "文件已下载", Toast.LENGTH_SHORT).show()
            }
        } catch (e: Exception) {
            Toast.makeText(context, "下载失败:${e.message}", Toast.LENGTH_SHORT).show()
            e.printStackTrace()
        }
    }
}

2. 配置WebView并改造下载逻辑

// 初始化WebView时配置
binding?.webView?.apply {
    settings.javaScriptEnabled = true
    addJavascriptInterface(PdfDownloadInterface(this@YourActivity), "PdfDownloader")
    
    setDownloadListener { url, _, contentDisposition, mimeType, _ ->
        if (url.startsWith("blob:")) {
            // 针对Blob URL,调用JS转换为Base64后下载
            val fileName = URLUtil.guessFileName(url, contentDisposition, mimeType)
            val jsCode = """
                var xhr = new XMLHttpRequest();
                xhr.open('GET', '$url', true);
                xhr.responseType = 'blob';
                xhr.onload = function(e) {
                    if (this.status == 200) {
                        var blob = this.response;
                        var reader = new FileReader();
                        reader.onloadend = function() {
                            var base64Data = reader.result.split(',')[1];
                            PdfDownloader.downloadPdf(base64Data, '$fileName');
                        }
                        reader.readAsDataURL(blob);
                    }
                };
                xhr.send();
            """.trimIndent()
            evaluateJavascript(jsCode, null)
        } else {
            // 普通URL保留原DownloadManager逻辑
            if (storagePermission()) {
                val request = DownloadManager.Request(Uri.parse(url))
                request.setMimeType(mimeType)
                request.allowScanningByMediaScanner()
                request.setDescription("Downloading file...")
                val fileName = URLUtil.guessFileName(url, contentDisposition, mimeType)
                request.setTitle(fileName)
                request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
                request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)
                val dm = context.getSystemService(DOWNLOAD_SERVICE) as DownloadManager
                dm.enqueue(request)
            }
        }
    }
}

3. 权限配置

在AndroidManifest.xml中添加存储权限(针对Android 10及以下):

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" />

替代方案说明

如果JS转换方案存在兼容性问题,也可通过WebViewClient的shouldInterceptRequest方法拦截Blob请求,直接获取文件流保存,但实现复杂度更高,优先推荐Base64转换方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 21:45:46