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

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š

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 13:47:02