原链接:http://blog.saymagic.cn/2015/11/08/webview-upload.html?utm_source=tuicool&utm_medium=referral

从零开始

我们在xml中写入一个简单的Webview组件:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
  3. android:layout_height="match_parent"
  4. tools:context=".MainActivity">
  5. <WebView
  6. android:id="@+id/webview"
  7. android:layout_width="fill_parent"
  8. android:layout_height="fill_parent"
  9. android:layout_margin="5dp"></WebView>
  10. </RelativeLayout>

然后在Java代码中使用其加载一个能够提供上传服务的URL:

  1. WebView webview = (WebView) findViewById(R.id.webview);
  2. webview.loadUrl(A_UPLOAD_URL);

之后,要加网络权限:

  1. <uses-permission android:name="android.permission.INTERNET"></uses-permission>

如果想让Webview能够访问本地资源,SD卡的读写权限也是避免不了的:

  1. <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中参数是不同的,为此我们一般需要写三个重载函数,大致像这个样子:

  1. private ValueCallback<Uri> mUploadMessage;
  2. //设置`WebChromeClient`:
  3. webview.setWebChromeClient(new WebChromeClient(){
  4. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
  5. Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg)");
  6. mUploadMessage = uploadMsg;
  7. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  8. i.addCategory(Intent.CATEGORY_OPENABLE);
  9. i.setType("*/*");
  10. MainActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
  11. }
  12. public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
  13. Log.d(TAG, "openFileChoose( ValueCallback uploadMsg, String acceptType )");
  14. mUploadMessage = uploadMsg;
  15. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  16. i.addCategory(Intent.CATEGORY_OPENABLE);
  17. i.setType("*/*");
  18. MainActivity.this.startActivityForResult(
  19. Intent.createChooser(i, "File Browser"),
  20. FILECHOOSER_RESULTCODE);
  21. }
  22. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
  23. Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");
  24. mUploadMessage = uploadMsg;
  25. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  26. i.addCategory(Intent.CATEGORY_OPENABLE);
  27. i.setType("*/*");
  28. MainActivity.this.startActivityForResult( Intent.createChooser( i, "File Browser" ), MainActivity.FILECHOOSER_RESULTCODE );
  29. }
  30. });
  31. //onActivityResult回调
  32. @Override
  33. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  34. super.onActivityResult(requestCode, resultCode, data);
  35. if(requestCode==FILECHOOSER_RESULTCODE)
  36. {
  37. if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
  38. Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
  39. if (mUploadMessage != null) {
  40. mUploadMessage.onReceiveValue(result);
  41. mUploadMessage = null;
  42. }
  43. }
  44. }

还有重要的一点:如果这个上传操作涉及到JS操作,别忘记对Webview开启对JS的支持:

  1. WebSettings settings = webview.getSettings();
  2. settings.setJavaScriptEnabled(true);

这样,打个debug包测试看以下,不出意外我们的Webview应该可以支持上传操作了。

别高兴得太早,如果这个时候产品要将release包推向市场,当你把release包交给产品时,你会发现你的Webview又不能上传了,什么情况?

请听Webview上传操作的第二个坑。

支持release版

debug版是好的,为什么release就不行了呢?准确的说,开启了混淆的release包是不可以的,究其原因在于,openFileChooser 方法并不是WebChromeClient 的对外开放的方法,因此这个方法会被混淆,解决办法也比较简单,只需要在混淆文件里控制一下即可:

  1. -keepclassmembers class * extends android.webkit.WebChromeClient{
  2. public void openFileChooser(...);
  3. }

好了,我们的Webview可以作为应用内的一个部分对外发布了,等等,有5.0以上用户反映用不了?纳尼????

别回心,来看看这第三个坑。

支持5.0

在5.0发布后,Android人家说了,这次我们回调的不是openFileChooser方法,而是onShowFileChooser方法,并且上文提到的ValueCallback参数里包裹着不再是Uri,而是Uri数组,因此我们必须为5.0+的机器做适配,大致思路如下:

  1. webview.setWebChromeClient(new WebChromeClient(){
  2. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
  3. ...
  4. }
  5. public void openFileChooser( ValueCallback uploadMsg, String acceptType ) {
  6. ...
  7. }
  8. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
  9. ...
  10. }
  11. // For Android 5.0+
  12. public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
  13. mUploadCallbackAboveL = filePathCallback;
  14. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  15. i.addCategory(Intent.CATEGORY_OPENABLE);
  16. i.setType("*/*");
  17. MainActivity.this.startActivityForResult(
  18. Intent.createChooser(i, "File Browser"),
  19. FILECHOOSER_RESULTCODE);
  20. return true;
  21. }
  22. });
  23. @Override
  24. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  25. super.onActivityResult(requestCode, resultCode, data);
  26. if(requestCode==FILECHOOSER_RESULTCODE)
  27. {
  28. if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
  29. Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
  30. if (mUploadCallbackAboveL != null) {
  31. onActivityResultAboveL(requestCode, resultCode, data);
  32. }
  33. else if (mUploadMessage != null) {
  34. mUploadMessage.onReceiveValue(result);
  35. mUploadMessage = null;
  36. }
  37. }
  38. }
  39. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  40. private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
  41. if (requestCode != FILECHOOSER_RESULTCODE
  42. || mUploadCallbackAboveL == null) {
  43. return;
  44. }
  45. Uri[] results = null;
  46. if (resultCode == Activity.RESULT_OK) {
  47. if (data == null) {
  48. } else {
  49. String dataString = data.getDataString();
  50. ClipData clipData = data.getClipData();
  51. if (clipData != null) {
  52. results = new Uri[clipData.getItemCount()];
  53. for (int i = 0; i < clipData.getItemCount(); i++) {
  54. ClipData.Item item = clipData.getItemAt(i);
  55. results[i] = item.getUri();
  56. }
  57. }
  58. if (dataString != null)
  59. results = new Uri[]{Uri.parse(dataString)};
  60. }
  61. }
  62. mUploadCallbackAboveL.onReceiveValue(results);
  63. mUploadCallbackAboveL = null;
  64. return;
  65. }

如上,我们的Webview应该就可以适应5.0+的机器了。

WebView 上传文件 WebChromeClient之openFileChooser函数的更多相关文章

  1. Android webview 上传文件不调用openFileChooser解决办法

    html页面带有图片上传功能,关于使用openFileChooser方法去选择图片,并且在onActivityResult方法里面设置返回的图片url文件路径,网上有很多,再次不再赘述. 实践中发现, ...

  2. android使用webview上传文件(支持相册和拍照)

    老夫最近需要做一个项目,需要调用服务器段的一些网页来选择文件,刚开始还挺纠结的,不知从何下手,网上大致预览了大神们走过的路,他们传统的方式都是使用一下代码: public void openFileC ...

  3. Android应用开发中webview上传文件的几种思路

    1. 常规方法,重写WebChromeClient 的 openFileChooser 方法 private class MyWebChromeClient extends WebChromeClie ...

  4. 关于Android WebView上传文件的解决方案

    我们在开发需求的时候,难免会接入一下第三方的H5页面,有些H5页面是具有上传照片的功能,Android 中的 WebView是不能直接打开文件选择弹框的 接下来我讲简单提供一下解决方案,先说一下思路 ...

  5. phpcms前端页面上传文件

    PHPCMS其实有一个叫做附件的模块,上传用的就是这个东西,现在我们来看一下对应的文件:phpcms\modules\attachment \attachments.php就是这个文件,大概在29行上 ...

  6. Angular2使用ng2-file-upload上传文件

    Angular2中有两个比较好用的上传文件的第三方库,一个是ng2-file-upload,一个是ng2-uploader.ng2-uploader是一个轻便的上传文件的支持库,功能较弱,而ng2-f ...

  7. webview 本地上传文件

    参考http://blog.csdn.net/zhtsuc/article/details/49154099 直接上代码   public class MainActivity1 extends Ac ...

  8. Android WebView 上传各种文件(包括拍照 录像 录音 文件 音乐 等,用到图片或拍照的,可以参考下)

    我也是从网上扒下来的,经过多次实验,找到了个好用的.网上能搜到最多的也就是这个解决方案,我英文不好,也没仔细研究,但大多数都是出自这: http://stackoverflow.com/questio ...

  9. 小程序云开发--云函数上传文件或图片 base64

    云函数开发遇到的问题 在微信云开发环境当中,普通的用户并没有往云存储内写入文件的权限 所以普通用户想要使用wx.cloud.uploadFile显然是不现实的 但是我们同时也知道,云函数是后台服务端, ...

随机推荐

  1. PHP禁止同一IP频繁访问以防止网站被防攻击或采集的代码

    PHP禁止同一IP频繁访问以防止网站被防攻击或采集的代码 <?php /* *通过禁止IP频繁访问防止网站被防攻击代码*design by www.scutephp.com*/header('C ...

  2. sql server之临时表

    一.临时表介绍 临时表在运行被创建的,虽说它的名字叫做临时表,可是对它的操作和我们平时操控表的操作基本一样, 比如最简单增.删.改.查等等.但是,需要注意的是临时表的创建是有范围限制的. 二.临时表分 ...

  3. lisp中的nil

    ANSI Common Lisp 第二章 课后习题 7.只使用本章所介绍的操作符,定义一个函数,它接受一个列表作为实参,如果有一个元素是列表时,就返回真. (defun have-list (lst) ...

  4. 解决SOAPCLIENT访问WebSerivce外网发布端口

    猫用vs2010写了一个webservice,并写了一个盘点程序客户端,PDA盘点机用C#开发,笔记本用VFP开发,发布在本地局域网IIS服务器,用了两年一直很稳定.后面仓库搬迁,需要外网进行访问,在 ...

  5. fedora 24下修改IP

    在ROOT环境下 cd /etc/sysconfig/network-scripts 找到类似  ifcfg-enp1s0的文件 sudo vi ifcfg-enp1s0 HWADDR=XX:XX:X ...

  6. ES 集群

    http://www.cnblogs.com/o-andy-o/p/5067184.html

  7. 郑捷《机器学习算法原理与编程实践》学习笔记(第四章 推荐系统原理)(二)kmeans

    (上接第二章) 4.3.1 KMeans 算法流程 算法的过程如下: (1)从N个数据文档随机选取K个文档作为质心 (2)对剩余的每个文档测量其到每个质心的距离,并把它归到最近的质心的类 (3)重新计 ...

  8. 检查项目里是否有IDFA的方法

    检查项目里是否有IDFA的方法: 步骤:1.打开终端cd到要检查的文件的根目录. 2.执行下列语句:grep -r advertisingIdentifier . (别少了最后那个点号). 发现有ma ...

  9. Android WIFI 分析(二)

    本文介绍Wifi 分析线路二:在Setting中打开WiFi功能.扫描网络以及连接网络的流程. WifiSettings 无线网络设置界面 WifiEnabler 相当于无线网络设置开关 WifiDi ...

  10. python中获取今天昨天和明天的日期

    import datetime today = datetime.date.today()oneday = datetime.timedelta(days=1)yesterday = today-on ...