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

