【flutter】webview下载文件方法集锦

说明:android的webview是不支持下载的!!!

所以我们需要监听下载接口 然后手动执行下载操作,分为三种类型

  1. 直接打开浏览器下载(最简单),但是一些下载接口需要cookie信息时不能满足
/*** 下载文件选择指定应用下载*/webView.setDownloadListener(new DownloadListener() {@Overridepublic void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {Uri uri = Uri.parse(url);Intent intent = new Intent(Intent.ACTION_VIEW, uri);activity.startActivity(intent);}});
  1. 在后台内置下载(此时还是在监听里面执行下载操作,还不算复杂)
//在webview属性设置处添加webView.setDownloadListener(new DownloadListener() {@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Overridepublic void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {if(URLUtil.isNetworkUrl(url)){// 解析 contentDisposition 以获取文件名String fileName = contentDisposition.split("filename=")[1].trim().replace("\"", "");try {fileName = URLDecoder.decode(fileName, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}downloadUrl(url, fileName);} }});//下载方法private void downloadUrl(String url, final String fileName) {//获取cookieCookieManager cookieManager = CookieManager.getInstance();String cookie = cookieManager.getCookie(url);DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);request.setTitle(fileName); // 使用文件名作为标题request.setDescription("Downloading " + fileName + "...");request.setVisibleInDownloadsUi(true); // 在下载管理器中显示下载if (cookie != null) {// 处理获取到的 Cookie
//            Log.d("Cookie", "Cookie for URL " + url + ": " + cookie);request.addRequestHeader("Cookie", cookie);} else {Log.d("Cookie", "No cookie found for URL " + url);}request.setMimeType("application/octet-stream");// 设置下载路径和文件名request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName);// 获取下载管理器并开始下载DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);downloadManager.enqueue(request);final Handler handler = new Handler(Looper.getMainLooper());handler.post(new Runnable() {@Overridepublic void run() {Toast.makeText(context, "下载完成,请到通知栏点击安装或打开文件", Toast.LENGTH_SHORT).show();}});}
  1. 监听下载已经无法满足下载:如果浏览器是以下载接口请求后生成 blob:https:下载地址,去获取浏览器存储的文件,在监听里便无法直接下载原文件,因为无法获取到响应头信息,因此无法获取文件名来保存文件。当然如果知道下载的文件类型 可以 通过自定义文件名方式来下载blob流文件。

    1. 下载blob流文件:有固定的文件类型 或者通过魔法字符获取文件类型,仍在下载监听内下载
      实现思路:
      (1)判断为blob下载
      webView.setDownloadListener(new DownloadListener() {@RequiresApi(api = Build.VERSION_CODES.KITKAT)@Overridepublic void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {if (url.startsWith("blob:")) {// 获取文件名String fileName = URLUtil.guessFileName(url, contentDisposition, mimetype);// 获取 blob 数据并写入文件 已经在拦截地方下载downloadBlobFile(url, fileName);}}});

(2)定义下载事件,通过调用js请求 blob接口,获取返回数据 回传给android (需要先设置监听才能收到返回数据)

	//下载方法定义@RequiresApi(api = Build.VERSION_CODES.KITKAT)private void downloadBlobFile(String blobUrl, final String fileName) {// 由于 WebView 不能直接处理 blob URL,我们需要使用 JavaScript 接口或其他方法来获取 blob 数据// 以下代码是一个示例,展示了如何使用 WebView 获取 blob 数据并写入文件webView.evaluateJavascript("console.log('开始请求blob...');" +"var xhr = new XMLHttpRequest();" +"xhr.open('GET', '"+blobUrl+"', true);" +"xhr.responseType = 'blob';" +"xhr.onload = function() {" +"  if (this.status === 200) {" +"    const blob = this.response;" +"    var reader = new FileReader();" +"    reader.readAsDataURL(blob);" +"    reader.onloadend = function() {" +"      var base64data = reader.result.split(',')[1];" +"      console.log('base64data:'+base64data);" +"      Android.androidCallback( base64data);" +"    };" +"  } else {" +"    console.error('Failed to fetch blob data');" +"  }" +"};" +"xhr.onerror = function() {" +"  console.error('Error fetching blob data');" +"};" +"xhr.send();", null);}

(3)保存文件

	//在webview设置处设置监听webView.addJavascriptInterface(new WebAppInterface(context), "Android");//定义APP监听接口 获取传回的数据public class WebAppInterface {Context mContext;WebAppInterface(Context c) {mContext = c;}@RequiresApi(api = Build.VERSION_CODES.O)@JavascriptInterfacepublic void androidCallback(String message) {// 在这里处理从 JavaScript 传递过来的数据Toast.makeText(mContext, "Received message: " + message, Toast.LENGTH_SHORT).show();String base64Data = message;//保存成文件saveBase64ToFile(base64Data, "downloaded_file");}}//保存文件方法定义@RequiresApi(api = Build.VERSION_CODES.O)private void saveBase64ToFile(String base64Data, String fileName) {try {byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);File file = new File(context.getFilesDir(), fileName);FileOutputStream outputStream = new FileOutputStream(file);outputStream.write(decodedBytes);outputStream.close();//在通知栏显示下载内容方便用户打开文件showDownloadNotification(file);} catch (IOException e) {e.printStackTrace();}}

(4)显示下载通知

    //通知方法定义@RequiresApi(api = Build.VERSION_CODES.O)private void showDownloadNotification(File file) throws IOException {NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {NotificationChannel channel = new NotificationChannel("file_open", "File Open", NotificationManager.IMPORTANCE_DEFAULT);notificationManager.createNotificationChannel(channel);}// 创建一个 Intent 来打开文件Intent intent = new Intent(Intent.ACTION_VIEW);String mimeType = Files.probeContentType(Paths.get(file.getPath()));
// 创建一个文件提供者 URIUri fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".flutterWebviewPluginProvider", file);intent.setDataAndType(fileUri, mimeType); // 设置文件的 MIME 类型intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 授予临时权限// 创建 PendingIntentPendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);long[] pattern = {0, 100, 200, 300};Notification notification = new NotificationCompat.Builder(context, "file_open").setSmallIcon(R.drawable.ic_download).setContentTitle(file.getName()).setContentText("下载完成,点击打开文件").setPriority(NotificationCompat.PRIORITY_DEFAULT).setContentIntent(pendingIntent) // 设置点击通知后执行的操作.setAutoCancel(true) // 点击后自动取消通知.build();notificationManager.notify(1, notification);}

2、 监听所有接口请求,过滤出下载接口,自定义下载
(1)设置setWebViewClientwebView.setWebViewClient(webViewClient);
(2)定义webViewClient
(3)过滤出下载接口后,先发起请求获取文件名信息,再进行下载操作

  	 webViewClient = new BrowserClient() {/*监听所有接口请求,过滤所有下载请求*/@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)@Overridepublic WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {String url = request.getUrl().toString();if (isDownloadUrl(url)) {Map<String, String> requestHeaders= request.getRequestHeaders();String acceptHeaderValue = requestHeaders.get("Accept");if(acceptHeaderValue != null && acceptHeaderValue.contains("image/")){//忽略页面上img地址为下载接口return super.shouldInterceptRequest(view, request); // 拦截请求}// 处理下载请求startDownloadWithDisposition(url);return null; // 拦截请求}return super.shouldInterceptRequest(view, request);}private boolean isDownloadUrl(String url) {// 检查 URL 是否是下载 URL,例如检查文件扩展名或 MIME 类型return url.contains("downloadFile"); // 示例:检查接口地址}/*先请求获取响应头获取文件名后再进行下载操作*/private void startDownloadWithDisposition(String urlString) {try {URL url = new URL(urlString);HttpURLConnection connection = (HttpURLConnection) url.openConnection();connection.setRequestMethod("GET"); // 只请求头部信息 HEAD请求的响应头数据不全 所以用GET// 获取 Content-Disposition 头String contentDisposition = connection.getHeaderField("Content-Disposition");// 解析文件名String fileName = parseFileNameFromContentDisposition(contentDisposition);fileName = URLDecoder.decode(fileName, "UTF-8");// 断开连接connection.disconnect();downloadUrl(urlString,fileName);} catch (IOException e) {e.printStackTrace();}}/*获取文件名*/private String parseFileNameFromContentDisposition(String contentDisposition) {if (contentDisposition == null) {return null;}String[] parts = contentDisposition.split(";");for (String part : parts) {if (part.trim().startsWith("filename=")) {return part.substring("filename=".length()).trim().replace("\"", "");}}return null;}};
注意:获取到的文件名需转码为utf8
fileName = URLDecoder.decode(fileName, "UTF-8");

补充:
(1)okhttp引入报红时,需要在你的webview插件导入 okhttp包

//webview_plugin\android\build.gradle中dependencies {implementation 'com.squareup.okhttp3:okhttp:3.11.0'}

(2)消息通知引入 自定义图标.setSmallIcon(R.drawable.ic_download)
在webview插件的资源部中引入webview_plugin\android\src\main\res\
在这里插入图片描述

(3)通过请求头accept 来判断是否img等资源文件下载还是接口请求 资源文件下载要忽略掉不进行下载操作

 				Map<String, String> requestHeaders= request.getRequestHeaders();String acceptHeaderValue = requestHeaders.get("Accept");if(acceptHeaderValue != null && acceptHeaderValue.contains("image/")){//忽略页面上img地址为下载接口return super.shouldInterceptRequest(view, request); // 拦截请求}

通常情况下:

  1. 加载API接口:API接口一般返回JSON、XML或其他结构化数据格式,所以Accept头部可能会包含类似application/json、application/xml等值。
  2. 加载图片:对于图片资源,Accept头部则会指向图像相关的MIME类型,如image/jpeg、image/png、image/gif等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/487741.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java版-图论-最短路-Floyd算法

实现描述 网络延迟时间示例 根据上面提示&#xff0c;可以计算出&#xff0c;最大有100个点&#xff0c;最大耗时为100*wi,即最大的耗时为10000&#xff0c;任何耗时计算出来超过这个值可以理解为不可达了&#xff1b;从而得出实现代码里面的&#xff1a; int maxTime 10005…

SQL注入基础入门篇 注入思路及常见的SQL注入类型总结

目录 前言一、了解mysql数据库1、了解sql增删改查2、了解sql查询 二、sql注入基础三、学习sql注入漏洞1、union注入1、判断数字型注入还是字符型型注入&#xff1a;2、判断闭合方式&#xff08;字符型注入&#xff09;&#xff1a;3、判断回显位4、查询库名&#xff0c;表名&am…

基于Spring Boot库存管理系统

文末获取源码和万字论文&#xff0c;制作不易&#xff0c;感谢点赞支持。 基于Spring Boot库存管理系统 当下&#xff0c;如果还依然使用纸质文档来记录并且管理相关信息&#xff0c;可能会出现很多问题&#xff0c;比如原始文件的丢失&#xff0c;因为采用纸质文档&#xff0c…

JSSIP的使用及问题(webRTC,WebSockets)

简介 项目中有一个需要拨打电话的功能&#xff0c;要求实时的进行音频接听&#xff0c;并且可以在电话接听或者挂断等情况下做出相应的操作。jssip作为一个强大的实现实时通信的javascript库&#xff0c;这不门当户对了嘛。 jssip&#xff08;官网&#xff1a; JsSIP - the J…

【Cadence32】PCB多层板电源、地平面层创建心得➕CM约束管理器Analyze分析显示设置➕“DP”报错DRC

【转载】Cadence Design Entry HDL 使用教程 【Cadence01】Cadence PCB Edit相对延迟与绝对延迟的显示问题 【Cadence02】Allegro引脚焊盘Pin设置为透明 【Cadence03】cadence不小心删掉钢网层怎么办&#xff1f; 【Cadence04】一般情况下Allegro PCB设计时的约束规则设置&a…

Java阶段三06

第3章-第6节 一、知识点 理解MVC三层模型、理解什么是SpringMVC、理解SpringMVC的工作流程、了解springMVC和Struts2的区别、学会使用SpringMVC封装不同请求、接收参数 二、目标 理解MVC三层模型 理解什么是SpringMVC 理解SpringMVC的工作流程 学会使用SpringMVC封装请求…

C/C++流星雨

系列文章 序号直达链接1C/C爱心代码2C/C跳动的爱心3C/C李峋同款跳动的爱心代码4C/C满屏飘字表白代码5C/C大雪纷飞代码6C/C烟花代码7C/C黑客帝国同款字母雨8C/C樱花树代码9C/C奥特曼代码10C/C精美圣诞树11C/C俄罗斯方块12C/C贪吃蛇13C/C孤单又灿烂的神-鬼怪14C/C闪烁的爱心15C/C…

Vmware Vcenter7.0证书web续期发生错误

1. 故障描述 vSphere Client 版本 7.0.2.00200 vCenter _MACHINE_CERT快到期了&#xff0c;通过web界面更新证书失败 第一步先这样&#xff0c;重新续订一下证书 续订发生错误 2. 解决办法 2.1. 前提工作 登陆ssh到vcenter&#xff0c;重新生成证书 先关掉HA&#xff…

【合作原创】使用Termux搭建可以使用的生产力环境(五)

前言 在上一篇【合作原创】使用Termux搭建可以使用的生产力环境&#xff08;四&#xff09;-CSDN博客我们讲到了如何让proot-distro中的Debian声音驱动正常&#xff0c;将我们的系统备份后&#xff0c;通过VNC客户端连接到VNC服务器&#xff0c;这一篇我们来讲一下xfce桌面的美…

uniapp -- 实现页面滚动触底加载数据

效果 首选,是在pages.json配置开启下拉刷新 {"path": "pages/my/document/officialDocument","style": {"navigationStyle":</

Python之爬虫入门--示例(2)

一、Requests库安装 可以使用命令提示符指令直接安装requests库使用 pip install requests 二、爬取JSON数据 &#xff08;1&#xff09;、点击网络 &#xff08;2&#xff09;、刷新网页 &#xff08;3&#xff09;、这里有一些数据类型&#xff0c;选择全部 &#xff08…

OLLAMA+FASTGPT+M3E 大模型本地化部署手记

目录 1.安装ollama 0.5.1 2.下载大模型 qwen2.5 3b 3.开启WSL 4.更新wsl 5.安装ubuntu 6.docker下载 6.1 修改docker镜像源 6.2 开启WSL integration 7.安装fastgpt 7.1 创建fastgpt文件夹 7.2 下载fastgpt配置文件 8.启动容器 9.M3E下载 9.1 下载运行命令 9.2…

Linux网络基础知识————网络编程

计算机网络的体系结构 网络采用分而治之的方法设计&#xff0c;将网络的功能划分为不同的模块&#xff0c;以分层的形式有机结合在一起 每层实现不同的功能&#xff0c;其内部实现的方法对外部其他层次来说是透明的&#xff0c;每层向上一层提供服务&#xff0c;使用下一层提供…

【数据库】选择题+填空+简答

1.关于冗余数据的叙述中&#xff0c;不正确的是&#xff08;&#xff09; A.冗余的存在容易破坏数据库的完整新 B.冗余的存在给数据库的维护增加困难 C.不应该在数据库中存储任何冗余数据 D.冗余数据是指由基本数据导出的数据 C 2.最终用户使用的数据视图称为&#xff08;&…

unity3d—demo(实现给出图集名字和图片名字生成对应的图片)

目录 实现给出图集名字和图片名字生成对应的图片&#xff1a; 代码示例&#xff1a; dic: 键 是图集名称 值是一个字典 该字典键是图片名称 值是图片&#xff0c;结构如图&#xff1a; 测试代码&#xff1a; 结果&#xff1a; SpriteRenderer 讲解&#xff1a; Resour…

jmeter 提取数据写入文件

BeanShell PostProcessor FileWriter file new FileWriter("E:\\IOT\\cui家庭中心\\v3.8.0\\123.txt",true); BufferedWriter out new BufferedWriter(file); out.write(vars.get("localKey")"\n"); log.info("到这里了吗"); out.c…

Linux多进程开发-常用命令

进程 进程是计算机中正在运行的程序的实例。每个进程都有自己的地址空间、内存、文件和设备、线程以及其他系统资源。操作系统通过调度和管理进程来实现多任务处理&#xff0c;使得多个进程可以同时运行并与用户交互。在操作系统中&#xff0c;进程是基本的资源分配单位&#x…

Linux下nginx环境的搭建

1.Nginx的下载 nginx官网&#xff1a;nginx: download nginx的工作原理请移步&#xff1a; 2.nginx安装 2.1.上传文件 将nginx包上传到 /usr/local/src 下 如果你没有使用xftp的话&#xff0c;使用 rz -y 命令上传 下载依赖&#xff1a; yum install lrzsz 输入命令 r…

支持图像和视频理解多模态开源大模型:CogVLM2 CogVLM2-Video

CogVLM2和CogVLM2-Video是新一代的开源模型&#xff0c;支持图像和视频理解&#xff0c;具有显著的性能提升。最近发布的更新包括CogVLM2论文的发表、在线演示和对视频理解的支持&#xff0c;能够处理最多1分钟的视频。新模型支持中英文&#xff0c;文本长度可达8K&#xff0c;…

vue中验证码的实现方式

在写登录页的时候有的系统会让你也进行一下验证码绘制&#xff0c;那么验证码如何实现的呢&#xff1f;我在写登录页的时候通过将登录框&#xff0c;验证码分开页面来写&#xff0c;最后将它们变成标签来导入到我的样式页面中&#xff0c;这样写不仅方便&#xff0c;更容易修改…