详解Android WebView的input上传照片的兼容问题

问题

前几天接到的一个需求,是关于第三方理财产品的H5上传照片问题。

对方说他们的新的需求,需要接入方配合上传资产照片的需求,测试之后发现我们这边的app端,IOS端上传没有问题,而Android端则点击没有任何反应。

对方H5调用的方式是通过<input type='file' accept='image/*'/>的方式调用,本来以为这个问题很简单,就是app端没有设置相机权限,造成的点击无反应情况,而实际上加了之后发现,并非简单的权限问题。

解决问题

因为Android的版本碎片问题,很多版本的WebView都对唤起函数有不同的支持。

我们需要重写WebChromeClient下的openFileChooser()(5.0及以上系统回调onShowFileChooser())。我们通过Intent在openFileChooser()中唤起系统相机和支持Intent的相关app。

在系统相机或者相关app中一顿操作之后,当返回app的时候,我们在onActivityResult()中将选择好的图片通过ValueCallback的onReceiveValue方法返回给WebView。

附上代码:

1、首先是重写各个版本的WebChromeClient的支持

webView.setWebChromeClient(new WebChromeClient() {
 //For Android 3.0+
 public void openFileChooser(ValueCallback<Uri> uploadMsg) {
   selectImage();
   mUM = uploadMsg;
   Intent i = new Intent(Intent.ACTION_GET_CONTENT);
   i.addCategory(Intent.CATEGORY_OPENABLE);
   i.setType("*/*");
   MyBaseWebViewActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), FCR);
 }

 // For Android 3.0+, above method not supported in some android 3+ versions, in such case we use this
 public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
   selectImage();
   mUM = uploadMsg;
   Intent i = new Intent(Intent.ACTION_GET_CONTENT);
   i.addCategory(Intent.CATEGORY_OPENABLE);
   i.setType("*/*");
   MyBaseWebViewActivity.this.startActivityForResult(
       Intent.createChooser(i, "File Browser"),
       FCR);
 }

 //For Android 4.1+
 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
   selectImage();
   mUM = uploadMsg;
   Intent i = new Intent(Intent.ACTION_GET_CONTENT);
   i.addCategory(Intent.CATEGORY_OPENABLE);
   i.setType("*/*");
   MyBaseWebViewActivity.this.startActivityForResult(Intent.createChooser(i, "File Chooser"), MyBaseWebViewActivity.FCR);
 }

 //For Android 5.0+
 public boolean onShowFileChooser(
     WebView webView, ValueCallback<Uri[]> filePathCallback,
     WebChromeClient.FileChooserParams fileChooserParams) {
   selectImage();
   if (mUMA != null) {
     mUMA.onReceiveValue(null);
   }
   mUMA = filePathCallback;
   Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
   if (takePictureIntent.resolveActivity(MyBaseWebViewActivity.this.getPackageManager()) != null) {
     File photoFile = null;
     try {
       photoFile = createImageFile();
       takePictureIntent.putExtra("PhotoPath", mCM);
     } catch (IOException ex) {
       Log.e(TAG, "Image file creation failed", ex);
     }
     if (photoFile != null) {
       mCM = "file:" + photoFile.getAbsolutePath();
       filePath = photoFile.getAbsolutePath();
       takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
     } else {
       takePictureIntent = null;
     }
   }
   Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT);
   contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE);
   contentSelectionIntent.setType("*/*");
   Intent[] intentArray;
   if (takePictureIntent != null) {
     intentArray = new Intent[]{takePictureIntent};
   } else {
     intentArray = new Intent[0];
   }

   Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER);
   chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent);
   chooserIntent.putExtra(Intent.EXTRA_TITLE, "Image Chooser");
   chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
   startActivityForResult(chooserIntent, FCR);
   return true;
 }
});

2、选完照片之后

/**
* 打开图库,同时处理图片
*/
private void selectImage() {
  compressPath = Environment.getExternalStorageDirectory().getPath() + "/QWB/temp";
  File file = new File(compressPath);
  if (!file.exists()) {
    file.mkdirs();
  }
  compressPath = compressPath + File.separator + "compress.png";
  File image = new File(compressPath);
  if (image.exists()) {
    image.delete();
  }
}

// Create an image file
private File createImageFile() throws IOException {
  @SuppressLint("SimpleDateFormat") String timeStamp = DateUtils.nowTimeDetail();
  String imageFileName = "img_" + timeStamp + "_";
  File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
  return File.createTempFile(imageFileName, ".jpg", storageDir);
}

private String mCM;
private String filePath = "";
private ValueCallback<Uri> mUM;
private ValueCallback<Uri[]> mUMA;
private final static int FCR = 1;
String compressPath = "";

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
  super.onActivityResult(requestCode, resultCode, intent);
  if (Build.VERSION.SDK_INT >= 21) {
    Uri[] results = null;
    //Check if response is positive
    if (resultCode == Activity.RESULT_OK) {
      if (requestCode == FCR) {
        if (null == mUMA) {
          return;
        }
        if (intent == null) {
          //Capture Photo if no image available
          if (mCM != null) {
            // results = new Uri[]{Uri.parse(mCM)};
            results = new Uri[]{afterChosePic(filePath, compressPath)};
          }
        } else {
          String dataString = intent.getDataString();
          if (dataString != null) {
            results = new Uri[]{Uri.parse(dataString)};
            LogUtil.d("tag", intent.toString());
//              String realFilePath = getRealFilePath(Uri.parse(dataString));
//              results = new Uri[]{afterChosePic(realFilePath, compressPath)};
          }
        }
      }
    }
    mUMA.onReceiveValue(results);
    mUMA = null;
  } else {
    if (requestCode == FCR) {
      if (null == mUM) return;
      Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData();
      mUM.onReceiveValue(result);
      mUM = null;
    }
  }
}

/**
* 选择照片后结束
*/
private Uri afterChosePic(String oldPath, String newPath) {
  File newFile;
  try {
    newFile = FileUtils.compressFile(oldPath, newPath);
  } catch (Exception e) {
    e.printStackTrace();
    newFile = null;
  }
  return Uri.fromFile(newFile);
}

3、工具类

public class FileUtils {
  /**
   * 把图片压缩到200K
   *
   * @param oldpath
   *      压缩前的图片路径
   * @param newPath
   *      压缩后的图片路径
   * @return
   */
  public static File compressFile(String oldpath, String newPath) {
    Bitmap compressBitmap = FileUtils.decodeFile(oldpath);
    Bitmap newBitmap = ratingImage(oldpath, compressBitmap);
    ByteArrayOutputStream os = new ByteArrayOutputStream();
    newBitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
    byte[] bytes = os.toByteArray();

    File file = null ;
    try {
      file = FileUtils.getFileFromBytes(bytes, newPath);
    } catch (Exception e) {
      e.printStackTrace();
    }finally{
      if(newBitmap != null ){
        if(!newBitmap.isRecycled()){
          newBitmap.recycle();
        }
        newBitmap = null;
      }
      if(compressBitmap != null ){
        if(!compressBitmap.isRecycled()){
          compressBitmap.recycle();
        }
        compressBitmap = null;
      }
    }
    return file;
  }

  private static Bitmap ratingImage(String filePath,Bitmap bitmap){
    int degree = readPictureDegree(filePath);
    return rotaingImageView(degree, bitmap);
  }

  /**
   * 旋转图片
   * @param angle
   * @param bitmap
   * @return Bitmap
   */
  public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {
    //旋转图片 动作
    Matrix matrix = new Matrix();;
    matrix.postRotate(angle);
    System.out.println("angle2=" + angle);
    // 创建新的图片
    Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0,
        bitmap.getWidth(), bitmap.getHeight(), matrix, true);
    return resizedBitmap;
  }

  /**
   * 读取图片属性:旋转的角度
   * @param path 图片绝对路径
   * @return degree旋转的角度
   */
  public static int readPictureDegree(String path) {
    int degree = 0;
    try {
      ExifInterface exifInterface = new ExifInterface(path);
      int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
      switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
          degree = 90;
          break;
        case ExifInterface.ORIENTATION_ROTATE_180:
          degree = 180;
          break;
        case ExifInterface.ORIENTATION_ROTATE_270:
          degree = 270;
          break;
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
    return degree;
  }

  /**
   * 把字节数组保存为一个文件
   *
   * @param b
   * @param outputFile
   * @return
   */
  public static File getFileFromBytes(byte[] b, String outputFile) {
    File ret = null;
    BufferedOutputStream stream = null;
    try {
      ret = new File(outputFile);
      FileOutputStream fstream = new FileOutputStream(ret);
      stream = new BufferedOutputStream(fstream);
      stream.write(b);
    } catch (Exception e) {
      // log.error("helper:get file from byte process error!");
      e.printStackTrace();
    } finally {
      if (stream != null) {
        try {
          stream.close();
        } catch (IOException e) {
          // log.error("helper:get file from byte process error!");
          e.printStackTrace();
        }
      }
    }
    return ret;
  }

  /**
   * 图片压缩
   *
   * @param fPath
   * @return
   */
  public static Bitmap decodeFile(String fPath) {
    BitmapFactory.Options opts = new BitmapFactory.Options();
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
    opts.inInputShareable = true; // Which kind of reference will be used to
    BitmapFactory.decodeFile(fPath, opts);
    final int REQUIRED_SIZE = 400;
    int scale = 1;
    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {
      final int heightRatio = Math.round((float) opts.outHeight
          / (float) REQUIRED_SIZE);
      final int widthRatio = Math.round((float) opts.outWidth
          / (float) REQUIRED_SIZE);
      scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }
    Log.i("scale", "scal ="+ scale);
    opts.inJustDecodeBounds = false;
    opts.inSampleSize = scale;
    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(Bitmap.Config.ARGB_8888, false);
    return bm;
  }

  /**
   * 创建目录
   * @param path
   */
  public static void setMkdir(String path)
  {
    File file = new File(path);
    if(!file.exists())
    {
      file.mkdirs();
      Log.e("file", "目录不存在 创建目录  ");
    }else{
      Log.e("file", "目录存在");
    }
  }

  /**
   * 获取目录名称
   * @param url
   * @return FileName
   */
  public static String getFileName(String url)
  {
    int lastIndexStart = url.lastIndexOf("/");
    if(lastIndexStart!=-1)
    {
      return url.substring(lastIndexStart+1, url.length());
    }else{
      return null;
    }
  }

  /**
   * 删除该目录下的文件
   *
   * @param path
   */
  public static void delFile(String path) {
    if (!TextUtils.isEmpty(path)) {
      File file = new File(path);
      if (file.exists()) {
        file.delete();
      }
    }
  }
}

4、需要注意的问题

在打release包的时候,因为混淆的问题,点击又会没有反应,这是因为openFileChooser()是系统api,所以需要在混淆是不混淆该方法。

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

当点击拍照之后,如果相机是横屏拍照的话,当拍照结束之后跳回app的时候,会导致app端当前的webView页面销毁并重新打开,需要在androidManifest.xml中当前Activity添加:

android:configChanges="orientation|keyboardHidden|screenSize"

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android studio点击跳转WebView详解

    本文实例为大家分享了Android studio点击跳转WebView的具体代码,供大家参考,具体内容如下 代码文件 import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.

  • Android的WebView与H5前端JS代码交互的实例代码

    前段时间项目有深度和前端对接过,也是碰了一些坑,现在有时间就拿出来分享下 JS调用原生不外乎就两种,一种是传假的url,也就是url拦截的方式,类似于下面这种: //js代码 function sendCommand(param){ var url="js-call://"+param; document.location = url; } sendCommand("PlaySnake"); //Java代码 mWebView.setWebViewClient(ne

  • Android APP之WebView校验SSL证书的方法

    Android系统的碎片化很严重,并且手机日期不正确.手机根证书异常.com.google.android.webview BUG等各种原因,都会导致WebViewClient无法访问HTTPS站点.SSL错误的处理方式十分关键,如果处理不当,可能导致中间人攻击,黑客窃听数据,进而引发安全事故. 严谨地处理onReceivedSslError尤为重要.请参考以下代码,原理是:如果webview报告SSL错误,程序将会对服务器证书进行强校验,如果服务器传入证书的指纹(sha256)与记录值一致,说

  • Android webview手动校验https证书(by 星空武哥)

    有些时候由于Android系统的bug或者其他的原因,导致我们的webview不能验证通过我们的https证书,最明显的例子就是华为手机mate7升级到Android7.0后,手机有些网站打不开了,而更新了webview的补丁后就没问题了,充分说明系统的bug对我们混合开发webview加载https地址的影响是巨大的.那么我们怎么去解决这个问题呢? 首先我们去分析一下出现的原因 当webview加载https地址的时候,如果因为证书的问题出错的时候就会走onReceivedSslError()

  • Android 解决WebView调用loadData()方法显示乱码的问题

    Android 解决WebView调用loadData()方法显示乱码的问题 第一步: mWebView.getSettings().setDefaultTextEncodingName("UTF-8"); 第二步: mWebView.loadData(data, "text/html; charset=UTF-8", null); WebView常用配置: private void initWebView() { mWebView.getSettings().se

  • Android webview实现拍照的方法

    Android webview实现拍照的方法 1. html <div id="pnlVideo1"> <input type="hidden" name="imgNric1" id="imgNric1" /> <label id="nric" class="control-label labelfont" style="color:#888;fo

  • Android 中ViewPager中使用WebView的注意事项

    Android 中ViewPager中使用WebView的注意事项 前言: 今天在做项目时遇到了一个小问题 首先使用ViewPager显示多个页面,然后在每个页面上使用Fragment显示数据,其中有一部分数据是通过WebView加载的Html标签. 具体xml布局如下 <?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.andr

  • Android webview 内存泄露的解决方法

    Android webview 内存泄露的解决方法 最近在activity嵌套webview显示大量图文发现APP内存一直在涨,没法释放内存,查了很多资料,大概是webview的一个BUG,引用了activity导致内存泄漏,所以就尝试传递getApplicationContext. 1.避免在xml直接写webview控件,这样会引用activity,所以在xml写一个LinearLayout,然后 linearLayout.addView(new MyWebview(getApplicati

  • 详解Android WebView的input上传照片的兼容问题

    问题 前几天接到的一个需求,是关于第三方理财产品的H5上传照片问题. 对方说他们的新的需求,需要接入方配合上传资产照片的需求,测试之后发现我们这边的app端,IOS端上传没有问题,而Android端则点击没有任何反应. 对方H5调用的方式是通过<input type='file' accept='image/*'/>的方式调用,本来以为这个问题很简单,就是app端没有设置相机权限,造成的点击无反应情况,而实际上加了之后发现,并非简单的权限问题. 解决问题 因为Android的版本碎片问题,很多

  • 详解JavaWeb如何实现文件上传和下载功能

    目录 1. 文件传输原理及介绍 2. JavaWeb文件上传 2.1我们用一个新的方式创建项目 2.2 导包 2.3 实用类介绍 2.4 pom.xml导入需要的依赖 2.5 index.jsp 2.6 info.jsp 2.7 FileServlet 2.8 配置Servlet 2.9 测试结果 3. SpringMVC文件上传和下载 3.1 上传 3.2 下载 1. 文件传输原理及介绍 2. JavaWeb文件上传 2.1我们用一个新的方式创建项目 空项目会直接弹出框 把jdk版本设置好 点

  • 详解Android Webview加载网页时发送HTTP头信息

    详解Android Webview加载网页时发送HTTP头信息 当你点击一个超链接进行跳转时,WebView会自动将当前地址作为Referer(引荐)发给服务器,因此很多服务器端程序通过是否包含referer来控制盗链,所以有些时候,直接输入一个网络地址,可能有问题,那么怎么解决盗链控制问题呢,其实在webview加载时加入一个referer就可以了,如何添加呢? 从Android 2.2 (也就是API 8)开始,WebView新增加了一个接口方法,就是为了便于我们加载网页时又想发送其他的HT

  • 实例详解Android Webview拦截ajax请求

    Android Webview虽然提供了页面加载及资源请求的钩子,但是对于h5的ajax请求并没有提供干涉的接口,这意味着我们不能在webview中干涉javascript发起的http请求,而有时候我们确实需要能够截获ajax请求并实现一些功能如:统一的网络请求管理.cookie同步.证书校验.访问控制等. 思路 虽然在 Webview中无法直接拦截 ajax请求(其实在shouldInterceptRequest 中是可以收到ajax请求的,但是遗憾的是取不到请求参数,这样也是没有意义的),

  • 详解Node.js一行命令上传本地文件到服务器

    现在存在的问题 每次打包完, 都要打开 FileZilla 一顿拖拽然后才能上传代码, 那就立马撸一个自动化脚本就完事了 publish-sftp Github 传送门(顺便来骗个Star) 以后一行命令上传本地文件到服务器啦 publish-sftp -c // 完事 安全性 项目组已经跑了大半年, 没出过幺蛾子, 可放心使用 实现 基于 ssh2-sftp-client 快速上手 install sudo npm i publish-sftp -g sudo npm link publish

  • 详解Spring Boot中PATCH上传文件的问题

    Spring Boot中上传multipart/form-data文件只能是Post提交,而不针对PATCH,这个问题花了作者26个小时才解决这个问题,最后不得不调试Spring源代码来解决这个问题. 需求:在网页中构建一个表单,其中包含一个文本输入字段和一个用于文件上载的输入.很简单.这是表单: <form id="data" method="PATCH" action="/f" > <input type="tex

  • 实例详解SpringBoot+nginx实现资源上传功能

    最近小编在学习使用nginx放置静态资源,例如图片.视频.css/js等,下面就来记录一下一波学习干货. 1.nginx安装及配置 小编使用的服务器是阿里云的轻量应用服务器,系统使用的是Ubuntu.注意记得开放 9090TCP端口,如果不使用 9090端口作为服务器端口也可不用. 安装 首先,获取安装包是必要的吧,这里提供一个nginx-1.11.3-ubuntu.tar.gz https://pan.baidu.com/s/1vvb41QkOJ4VqfyFckXBkjA (密码45wz) 小

  • 详解Android WebView监听console错误信息

    根据需求,我们要拿到h5的错误信息,并将error信息进行上报.查询了下Android WebView的API发现了WebChromeClient这个方法可以满足要求: @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) { //获取log的级别 switch (consoleMessage.messageLevel()){ case ERROR://将error信息上报到服务端 LogUtil.log

  • 详解IOS开发中图片上传时两种图片压缩方式的比较

    IOS 图片上传时两种图片压缩方式的比较 上传图片不全面的想法:把图片保存到本地,然后把图片的路径上传到服务器,最后又由服务器把路径返回,这种方式不具有扩展性,如果用户换了手机,那么新手机的沙盒中就没有服务器返回的图片路径了,此时就无法获取之前已经上传了的头像了,在项目中明显的不可行. 上传图片的正确方式:上传头像到服务器一般是将图片NSData上传到服务器,服务器返回一个图片NSString地址,之后再将NSString的路径转为url并通过url请求去更新用户头像(用户头像此时更新的便是NS

  • 详解如何将本地项目上传到Github的方法步骤(图文)

    很早之前就注册了Github,但对其使用一直懵懵懂懂,很不熟练.直到昨天做完百度前端技术学院的task,想把代码托管到Github上的时候发现自己对于Git的操作是如此之愚钝,所以今天决定把Git好好学习一遍,好让自己以后能更好地使用Github,主要还是通过Git教程 - 廖雪峰的官方网站来学习.简要步骤可以直接看最后的总结. Git的安装就不说了. 第一步:我们需要先创建一个本地的版本库(其实也就是一个文件夹). 你可以直接右击新建文件夹,也可以右击打开Git bash命令行窗口通过命令来创

随机推荐