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

使用webview_flutter 4.0.7实现WebView文件下载遇问题求助

解决webview_flutter 4.0.7中Blob URL文件下载问题

你的问题核心是WebView遇到Blob格式的下载URL时无法正常处理:Blob URL是前端在本地生成的内存资源地址,并非远程HTTP链接,直接跳转会触发渲染报错,同时无法触发系统下载流程。以下是针对webview_flutter 4.0.7版本的具体解决方案:

核心思路

通过注入JavaScript拦截前端的下载操作,将Blob资源的内容、文件名、类型通过消息通道传递给Flutter端,由Flutter负责本地文件的保存与后续操作。

步骤1:配置WebView与消息通道

创建JavaScriptChannel用于接收Web端传递的文件数据,同时阻止Blob URL的默认跳转避免报错:

WebView(
  initialUrl: 'https://example.company_name.com',
  javascriptMode: JavascriptMode.unrestricted,
  // 建立与Web端的消息通道
  javascriptChannels: {
    JavascriptChannel(
      name: 'FileDownloadChannel',
      onMessageReceived: (JavascriptMessage message) {
        _handleFileDownload(message.message);
      },
    ),
  },
  onWebViewCreated: (WebViewController controller) {
    // 注入拦截下载的JS代码
    _injectDownloadInterceptor(controller);
  },
  // 拦截Blob URL的默认跳转
  onNavigationRequest: (NavigationRequest request) {
    if (request.url.startsWith('blob:')) {
      return NavigationDecision.prevent;
    }
    return NavigationDecision.navigate;
  },
)

步骤2:注入JavaScript拦截下载逻辑

通过runJavascript注入代码,拦截a标签下载和Blob跳转两种场景,将Blob转为Base64后传递给Flutter:

void _injectDownloadInterceptor(WebViewController controller) async {
  await controller.runJavascript('''
    // 拦截带download属性的a标签点击
    document.addEventListener('click', function(e) {
      const target = e.target.closest('a');
      if (target && target.href && target.download) {
        e.preventDefault();
        fetch(target.href)
          .then(res => res.blob())
          .then(blob => {
            const reader = new FileReader();
            reader.onloadend = function() {
              const base64 = reader.result.split(',')[1];
              FileDownloadChannel.postMessage(JSON.stringify({
                fileName: target.download,
                mimeType: blob.type,
                base64Data: base64
              }));
            };
            reader.readAsDataURL(blob);
          });
      }
    });

    // 拦截直接跳转的Blob URL
    window.addEventListener('beforeunload', function(e) {
      const url = window.location.href;
      if (url.startsWith('blob:')) {
        e.preventDefault();
        fetch(url)
          .then(res => res.blob())
          .then(blob => {
            const reader = new FileReader();
            reader.onloadend = function() {
              const base64 = reader.result.split(',')[1];
              // 自动生成文件名(可根据实际调整)
              const ext = blob.type.split('/')[1] || 'bin';
              FileDownloadChannel.postMessage(JSON.stringify({
                fileName: 'download.' + ext,
                mimeType: blob.type,
                base64Data: base64
              }));
            };
            reader.readAsDataURL(blob);
          });
      }
    });
  ''');
}

步骤3:Flutter端处理文件保存

接收Web端传递的数据,解码Base64后保存到本地存储:

import 'dart:convert';
import 'dart:io';
import 'package:path_provider/path_provider.dart';

void _handleFileDownload(String message) async {
  final data = jsonDecode(message);
  final fileName = data['fileName'];
  final base64Data = data['base64Data'];
  final bytes = base64Decode(base64Data);

  // 获取应用文档目录
  final dir = await getApplicationDocumentsDirectory();
  final file = File('${dir.path}/$fileName');

  // 写入文件
  await file.writeAsBytes(bytes);

  // 可选:提示用户或打开文件
  // 例如使用open_file包打开:OpenFile.open(file.path);
  // ScaffoldMessenger.of(context).showSnackBar(
  //   SnackBar(content: Text('文件已保存:${file.path}')),
  // );
}

注意事项

  • 必须开启JavascriptMode.unrestricted,否则注入的JS无法执行
  • 若前端下载逻辑不是a标签(如手动创建Blob URL下载),需调整JS拦截逻辑适配对应场景
  • 文件名优先使用前端传递的download属性,若没有则从MIME类型推导
  • Android需配置存储权限(如WRITE_EXTERNAL_STORAGE),iOS需确保文档目录访问权限
  • 大文件场景下Base64可能有性能损耗,建议优先让后端提供直接的HTTP下载链接

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 19:52:48