MAUI WebView中blob开头URL的文件下载异常解决方案咨询
MAUI WebView 处理 Blob 格式文件下载方案
针对你遇到的blob:https://...格式URL无法通过DownloadManager正常下载的问题,核心原因是Blob URL是浏览器内存中的临时对象引用,并非真实的网络资源地址,直接修改URL或用DownloadManager请求都会失败。以下是可行的解决方案:
解决方案思路
通过WebView与JavaScript的交互,从浏览器中直接读取Blob对象的二进制内容,再在C#端解码并保存为本地文件,具体步骤如下:
1. 修改CustomDownloadListener,区分Blob与普通URL
在OnDownloadStart方法中先判断URL是否为Blob格式,若是则触发JS读取逻辑,否则沿用原DownloadManager流程:
public class CustomDownloadListener : Java.Lang.Object, IDownloadListener { private readonly WebView _webView; // 通过构造函数传入WebView实例 public CustomDownloadListener(WebView webView) { _webView = webView; } public async void OnDownloadStart(string url, string userAgent, string contentDisposition, string mimetype, long contentLength) { try { if (url.StartsWith("blob:")) { // 处理Blob格式URL await HandleBlobDownload(url, contentDisposition, mimetype); } else { // 原有的普通URL下载逻辑 DownloadManager.Request request = new DownloadManager.Request(global::Android.Net.Uri.Parse(url)); request.SetMimeType(mimetype); request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted); string downloadDir = Path.Combine(FileSystem.AppDataDirectory, "download"); if (!Directory.Exists(downloadDir)) Directory.CreateDirectory(downloadDir); // 修正路径参数:第二个参数为null,第三个参数是完整相对路径 request.SetDestinationInExternalFilesDir(global::Android.App.Application.Context, null, Path.Combine("download", GetFileName(contentDisposition, url))); DownloadManager dm = (DownloadManager)global::Android.App.Application.Context.GetSystemService(global::Android.App.Application.DownloadService); dm.Enqueue(request); } } catch (Exception ex) { // 可添加异常日志记录逻辑 } } // 从contentDisposition或URL中提取文件名 private string GetFileName(string contentDisposition, string url) { // 解析contentDisposition,比如attachment;filename="example.pdf"格式 if (!string.IsNullOrEmpty(contentDisposition)) { var matches = System.Text.RegularExpressions.Regex.Match(contentDisposition, @"filename=""?([^""]+)""?"); if (matches.Success) { return matches.Groups[1].Value; } } // 解析失败时,从URL截取最后一段作为文件名 return Path.GetFileName(url); } }
2. 实现Blob内容读取与保存逻辑
添加HandleBlobDownload方法,通过JS获取Blob的Base64编码内容,再解码保存为本地文件:
private async Task HandleBlobDownload(string blobUrl, string contentDisposition, string mimetype) { // 执行JS代码读取Blob并转为Base64格式 string jsCode = $@" (async function() {{ try {{ const response = await fetch('{blobUrl}'); const blob = await response.blob(); return new Promise((resolve, reject) => {{ const reader = new FileReader(); reader.onloadend = () => resolve(reader.result); reader.onerror = reject; reader.readAsDataURL(blob); }}); }} catch (e) {{ return null; }} }})(); "; var result = await _webView.EvaluateJavaScriptAsync(jsCode); if (string.IsNullOrEmpty(result) || result == "null") { // JS执行失败,可添加错误提示 return; } // 去除Base64前缀(data:[mimetype];base64,)并解码 var base64Data = result.Replace("\"", "").Split(',')[1]; byte[] fileBytes = Convert.FromBase64String(base64Data); // 确定保存路径和文件名 string fileName = GetFileName(contentDisposition, blobUrl); string downloadDir = Path.Combine(FileSystem.AppDataDirectory, "download"); string filePath = Path.Combine(downloadDir, fileName); // 写入文件到本地 await File.WriteAllBytesAsync(filePath, fileBytes); // 可选:显示下载完成通知 ShowDownloadNotification(fileName, filePath); } // 可选:Android平台显示下载完成通知 private void ShowDownloadNotification(string fileName, string filePath) { var notificationManager = global::Android.App.Application.Context.GetSystemService(global::Android.Content.Context.NotificationService) as NotificationManager; if (notificationManager == null) return; string channelId = "MAUI_DOWNLOAD_CHANNEL"; if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { var channel = new NotificationChannel(channelId, "下载通知", NotificationImportance.Default); notificationManager.CreateNotificationChannel(channel); } var intent = new Intent(global::Android.App.Application.Context, typeof(MainActivity)); intent.PutExtra("filePath", filePath); var pendingIntent = PendingIntent.GetActivity(global::Android.App.Application.Context, 0, intent, PendingIntentFlags.Immutable); var notification = new Notification.Builder(global::Android.App.Application.Context, channelId) .SetContentTitle("下载完成") .SetContentText($"文件 {fileName} 已保存") .SetSmallIcon(global::Android.Resource.Drawable.IcDialogInfo) .SetContentIntent(pendingIntent) .SetAutoCancel(true) .Build(); notificationManager.Notify(1, notification); }
3. 绑定WebView与CustomDownloadListener
在MAUI的WebView初始化时,将CustomDownloadListener绑定到Android平台的原生WebView控件:
// 在页面的OnHandlerChanged方法中绑定 protected override void OnHandlerChanged() { base.OnHandlerChanged(); if (Handler?.PlatformView is Android.Webkit.WebView androidWebView) { // 替换为你的MAUI WebView实例 androidWebView.SetDownloadListener(new CustomDownloadListener(YourMauiWebViewInstance)); } }
关键说明
- Blob URL仅在当前WebView会话中有效,无法通过外部请求直接访问,必须通过浏览器内部JS API读取内容。
- 若Blob文件过大,转为Base64会占用较多内存,可考虑优化为分块读取逻辑,但Base64是当前MAUI WebView与JS交互最兼容的二进制传递方式。
- 原DownloadManager逻辑中
SetDestinationInExternalFilesDir的参数已修正,确保文件保存路径正确。
内容的提问来源于stack exchange,提问作者Timotej Arnuš
相关产品推荐
相关产品推荐

