WebView 上传文件 WebChromeClient之openFileChooser函数
原链接:http://blog.saymagic.cn/2015/11/08/webview-upload.html?utm_source=tuicool&utm_medium=referral
从零开始
我们在xml中写入一个简单的Webview组件:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><WebViewandroid:id="@+id/webview"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_margin="5dp"></WebView></RelativeLayout>
然后在Java代码中使用其加载一个能够提供上传服务的URL:
WebView webview = (WebView) findViewById(R.id.webview);webview.loadUrl(A_UPLOAD_URL);
之后,要加网络权限:
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
如果想让Webview能够访问本地资源,SD卡的读写权限也是避免不了的:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
最后,我们运行,会发现根本不能访问本地资源。Why?
让我们来填补第一个坑:
支持上传文件
Webview执行上传操作的逻辑是这样的:首先准备上传时会回调WebChromeClient类下的openFileChooser方法,在这个方法中给我们机会发起Intent来打开支持提供文件的第三方应用,最后在onActivityResult回调中将第三方应用提供的内容通过一个叫做ValueCallback的参数返回给Webview(详细点来说:ValueCallback是在openFileChooser 方法里由webview提供给我们的,里面包裹一个Uri,我们在onActivityResult 里将选中的Uri反馈给ValueCallback,这时候相当于Webview就知道我们选择了什么文件),因此,我们需要为Webview设置一个提供openFileChooser方法的WebChromeClient,这个方法在不同版本的Android中参数是不同的,为此我们一般需要写三个重载函数,大致像这个样子:
private ValueCallback<Uri> mUploadMessage;//设置`WebChromeClient`:webview.setWebChromeClient(new WebChromeClient(){public void openFileChooser(ValueCallback<Uri> uploadMsg) {Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg)");mUploadMessage = uploadMsg;Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("*/*");MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);}public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {Log.d(TAG, "openFileChoose( ValueCallback uploadMsg, String acceptType )");mUploadMessage = uploadMsg;Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("*/*");MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Browser"),FILECHOOSER_RESULTCODE);}public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");mUploadMessage = uploadMsg;Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("*/*");MainActivity.this.startActivityForResult( Intent.createChooser( i, "File Browser" ), MainActivity.FILECHOOSER_RESULTCODE );}});//onActivityResult回调@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if(requestCode==FILECHOOSER_RESULTCODE){if (null == mUploadMessage && null == mUploadCallbackAboveL) return;Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (mUploadMessage != null) {mUploadMessage.onReceiveValue(result);mUploadMessage = null;}}}
还有重要的一点:如果这个上传操作涉及到JS操作,别忘记对Webview开启对JS的支持:
WebSettings settings = webview.getSettings();settings.setJavaScriptEnabled(true);
这样,打个debug包测试看以下,不出意外我们的Webview应该可以支持上传操作了。
别高兴得太早,如果这个时候产品要将release包推向市场,当你把release包交给产品时,你会发现你的Webview又不能上传了,什么情况?
请听Webview上传操作的第二个坑。
支持release版
debug版是好的,为什么release就不行了呢?准确的说,开启了混淆的release包是不可以的,究其原因在于,openFileChooser 方法并不是WebChromeClient 的对外开放的方法,因此这个方法会被混淆,解决办法也比较简单,只需要在混淆文件里控制一下即可:
-keepclassmembers class * extends android.webkit.WebChromeClient{public void openFileChooser(...);}
好了,我们的Webview可以作为应用内的一个部分对外发布了,等等,有5.0以上用户反映用不了?纳尼????
别回心,来看看这第三个坑。
支持5.0
在5.0发布后,Android人家说了,这次我们回调的不是openFileChooser方法,而是onShowFileChooser方法,并且上文提到的ValueCallback参数里包裹着不再是Uri,而是Uri数组,因此我们必须为5.0+的机器做适配,大致思路如下:
webview.setWebChromeClient(new WebChromeClient(){public void openFileChooser(ValueCallback<Uri> uploadMsg) {...}public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {...}public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){...}// For Android 5.0+public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {mUploadCallbackAboveL = filePathCallback;Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("*/*");MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Browser"),FILECHOOSER_RESULTCODE);return true;}});@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if(requestCode==FILECHOOSER_RESULTCODE){if (null == mUploadMessage && null == mUploadCallbackAboveL) return;Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (mUploadCallbackAboveL != null) {onActivityResultAboveL(requestCode, resultCode, data);}else if (mUploadMessage != null) {mUploadMessage.onReceiveValue(result);mUploadMessage = null;}}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {if (requestCode != FILECHOOSER_RESULTCODE|| mUploadCallbackAboveL == null) {return;}Uri[] results = null;if (resultCode == Activity.RESULT_OK) {if (data == null) {} else {String dataString = data.getDataString();ClipData clipData = data.getClipData();if (clipData != null) {results = new Uri[clipData.getItemCount()];for (int i = 0; i < clipData.getItemCount(); i++) {ClipData.Item item = clipData.getItemAt(i);results[i] = item.getUri();}}if (dataString != null)results = new Uri[]{Uri.parse(dataString)};}}mUploadCallbackAboveL.onReceiveValue(results);mUploadCallbackAboveL = null;return;}
如上,我们的Webview应该就可以适应5.0+的机器了。
WebView 上传文件 WebChromeClient之openFileChooser函数的更多相关文章
- Android webview 上传文件不调用openFileChooser解决办法
html页面带有图片上传功能,关于使用openFileChooser方法去选择图片,并且在onActivityResult方法里面设置返回的图片url文件路径,网上有很多,再次不再赘述. 实践中发现, ...
- android使用webview上传文件(支持相册和拍照)
老夫最近需要做一个项目,需要调用服务器段的一些网页来选择文件,刚开始还挺纠结的,不知从何下手,网上大致预览了大神们走过的路,他们传统的方式都是使用一下代码: public void openFileC ...
- Android应用开发中webview上传文件的几种思路
1. 常规方法,重写WebChromeClient 的 openFileChooser 方法 private class MyWebChromeClient extends WebChromeClie ...
- 关于Android WebView上传文件的解决方案
我们在开发需求的时候,难免会接入一下第三方的H5页面,有些H5页面是具有上传照片的功能,Android 中的 WebView是不能直接打开文件选择弹框的 接下来我讲简单提供一下解决方案,先说一下思路 ...
- phpcms前端页面上传文件
PHPCMS其实有一个叫做附件的模块,上传用的就是这个东西,现在我们来看一下对应的文件:phpcms\modules\attachment \attachments.php就是这个文件,大概在29行上 ...
- Angular2使用ng2-file-upload上传文件
Angular2中有两个比较好用的上传文件的第三方库,一个是ng2-file-upload,一个是ng2-uploader.ng2-uploader是一个轻便的上传文件的支持库,功能较弱,而ng2-f ...
- webview 本地上传文件
参考http://blog.csdn.net/zhtsuc/article/details/49154099 直接上代码 public class MainActivity1 extends Ac ...
- Android WebView 上传各种文件(包括拍照 录像 录音 文件 音乐 等,用到图片或拍照的,可以参考下)
我也是从网上扒下来的,经过多次实验,找到了个好用的.网上能搜到最多的也就是这个解决方案,我英文不好,也没仔细研究,但大多数都是出自这: http://stackoverflow.com/questio ...
- 小程序云开发--云函数上传文件或图片 base64
云函数开发遇到的问题 在微信云开发环境当中,普通的用户并没有往云存储内写入文件的权限 所以普通用户想要使用wx.cloud.uploadFile显然是不现实的 但是我们同时也知道,云函数是后台服务端, ...
随机推荐
- go语言的selector
For a primary expression x that is not a package name, the selector expression x.f denotes the field ...
- gslX680驱动的移植实践
将gslX680触摸屏驱动移植到自己的开发板上(对应的源码文件gslX680.c),并且实现可以使用make menuconfig进行动态的加载和卸载 因为触摸屏设备属于一种典型的输入设备,所以他的驱 ...
- IELTS Writing Tips
- 关于tp.5.0角色管理导致的创建角色登陆报错问题解决!
今天用tp 5.0的时候,遇到一个问题,就是在利用超级管理员创建管理员角色时,角色账号密码登陆报错的问题 解决方法如下 htaccess文件修改如下 <IfModule mod_rewrite. ...
- python之路——基础篇(2)模块
模块:os.sys.time.logging.json/pickle.hashlib.random.re 模块分为三种: 自定义模块 第三方模块 内置模块 自定义模块 1.定义模块 将一系列功能函数或 ...
- 关于readdir返回值中struct dirent.d_type的取值有关问题(转)
关于readdir返回值中struct dirent.d_type的取值问题 原网页链接 http://www.gnu.org/software/libc/manual/html_node/Direc ...
- C语言调用curl库抓取网页图片(转)
思路是先用curl抓取网页源码,然后以关键字寻找出图片网址. 范例: #include <stdio.h> #include <stdlib.h> #include < ...
- chrome浏览器开发者工具之同步修改至本地
相信好多小伙伴喜爱webpack的热加载技术,省时而又不繁琐,讨厌F5或者Ctrl+F5. 嘿嘿,现在介绍大家一个在浏览器中修改直接同步到本地代码修改的方法--- (程序员都是从0开始数数的!) 第0 ...
- CSS3 animation 的尝试
下面是动画效果: .zoombie { width: 55px; height: 85px; background-image: url("http://images2015.cnblogs ...
- JavaBean的toString方法工具类
import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.r ...