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

