Android WebView嵌入H5之file头像上传文件,适配所有版本
最近公司需要开发新的项目,为了节省时间成本,要求整体嵌入H5界面。对,没看错是整体嵌入,心中一万只草泥马奔腾,没办法大佬就是大佬,还是的照做。
今天来说说h5上传头像的问题吧。网上有很多的解决方案,开始我觉得好像很简单,都有成功案例,等把代码copy下来,发现并不能运行成功,真是一脸懵逼...N脸懵逼。
先说说为什么别人的代码都说测试通过了而在我这里都不行了呢?
去google了一把,因为在4.4之后的内核不再是基于webkit内核,禁用了file域来读取手机本地文件,卧槽不是说向下兼容的么,兼容尼玛啊。上张图,
这就是为什么拍照之后并不能正确读取到数据。问题找到之后,那么解决就很好解决了。拍完照片之后,根据路径把图片插入到系统图库里面,完美解决。
File cameraFile = new File(mCameraFilePath);Bitmap bitmap1 = getimage(cameraFile.getPath());result = Uri.parse(MediaStore.Images.Media.insertImage(mContext.getContentResolver(), bitmap1, null, null));Log.e(TAG,"5.0-result="+result);uploadMessage.onReceiveValue(result);
下面在解决一下系统版本兼容的问题,需要重写WebChromeClient的以下方法,因为方法中有hide,你只需要重写就是。
// For Android < 3.0public void openFileChooser(ValueCallback<Uri> valueCallback) {uploadMessage = valueCallback;openImageChooserActivity();}// For Android >= 3.0public void openFileChooser(ValueCallback valueCallback, String acceptType) {uploadMessage = valueCallback;openImageChooserActivity();}//For Android >= 4.1public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {uploadMessage = valueCallback;openImageChooserActivity();}// For Android >= 5.0@Overridepublic boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {uploadMessageAboveL = filePathCallback;openImageChooserActivity();return true;}
看见有的同学,说重写不起作用,我暂时是没遇到的,如果遇到了再解决吧。下面贴一个webchromeclient 的完整代码,你只需要拷过去,简单调用,在添加以下权限就可以了。
package com.choe.webviewdemo2;import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebStorage;
import android.webkit.WebView;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;/*** @author cyk* @date 2016/12/14 14:02* @email choe0227@163.com* @desc* @modifier* @modify_time* @modify_remark*/
public class MyWebChromeClient extends WebChromeClient {private Activity mContext;private String mCameraFilePath;private ValueCallback<Uri> uploadMessage;private ValueCallback<Uri[]> uploadMessageAboveL;private final static String TAG="MyWebChromeClient";private final static int FILE_CHOOSER_RESULT_CODE = 10000;public MyWebChromeClient(Activity context) {mContext = context;}@Overridepublic void onProgressChanged(WebView view, int newProgress) {super.onProgressChanged(view, newProgress);}// For Android < 3.0public void openFileChooser(ValueCallback<Uri> valueCallback) {uploadMessage = valueCallback;openImageChooserActivity();}// For Android >= 3.0public void openFileChooser(ValueCallback valueCallback, String acceptType) {uploadMessage = valueCallback;openImageChooserActivity();}//For Android >= 4.1public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {uploadMessage = valueCallback;openImageChooserActivity();}// For Android >= 5.0@Overridepublic boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {uploadMessageAboveL = filePathCallback;openImageChooserActivity();return true;}@Override//扩容public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater) {quotaUpdater.updateQuota(requiredStorage*2);}@Overridepublic void onConsoleMessage(String message, int lineNumber, String sourceID) {Log.e("h5端的log", String.format("%s -- From line %s of %s", message, lineNumber, sourceID));}private void openImageChooserActivity() {initDialog();}/*** 上传头像时的弹出框*/private void initDialog(){new AlertDialog.Builder(mContext).setTitle("更改头像").setItems(new String[]{"拍照", "图库选取"},new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog,int which) {switch (which) {case 0:Intent i1=createCameraIntent();mContext.startActivityForResult(Intent.createChooser(i1, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);break;case 1:Intent i=createFileItent();mContext.startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);break;}}}).setNegativeButton("取消", null).show();}/*** 创建选择图库的intent* @return*/private Intent createFileItent(){Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("image/*");Intent intent = new Intent(Intent.ACTION_PICK,MediaStore.Images.Media.EXTERNAL_CONTENT_URI);intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,"image/*");return intent;}/*** 创建调用照相机的intent* @return*/private Intent createCameraIntent() {VersionUtils.checkAndRequestPermissionAbove23(mContext);Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);File externalDataDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);System.out.println("externalDataDir:" + externalDataDir);File cameraDataDir = new File(externalDataDir.getAbsolutePath()+ File.separator + "browser-photo");cameraDataDir.mkdirs();mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator+ System.currentTimeMillis() + ".jpg";System.out.println("mcamerafilepath:" + mCameraFilePath);cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,Uri.fromFile(new File(mCameraFilePath)));return cameraIntent;}/*** 处理拍照返回函数* @param requestCode* @param resultCode* @param data*/public void onActivityResult(int requestCode, int resultCode, Intent data){if (requestCode == FILE_CHOOSER_RESULT_CODE) {if (null == uploadMessage&& null == uploadMessageAboveL)return;Uri result = data == null || resultCode != Activity.RESULT_OK ? null: data.getData();if (uploadMessageAboveL != null) {//5.0以上onActivityResultAboveL(requestCode, resultCode, data);}else if(uploadMessage != null) {if (result == null && data == null&& resultCode == Activity.RESULT_OK) {File cameraFile = new File(mCameraFilePath);Bitmap bitmap1 = getimage(cameraFile.getPath());result = Uri.parse(MediaStore.Images.Media.insertImage(mContext.getContentResolver(), bitmap1, null, null));}Log.e(TAG,"5.0-result="+result);uploadMessage.onReceiveValue(result);uploadMessage = null;}}}/*** 处理拍照返回函数 5。0以上* @param requestCode* @param resultCode* @param intent*/@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {Log.e(TAG,"5.0+ 返回了");if (requestCode != FILE_CHOOSER_RESULT_CODE || uploadMessageAboveL == null)return;Uri[] results = null;if (resultCode == Activity.RESULT_OK) {if (intent != null) {String dataString = intent.getDataString();ClipData clipData = intent.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)};}else {File cameraFile = new File(mCameraFilePath);Bitmap bitmap1 = getimage(cameraFile.getPath());Uri result = Uri.parse(MediaStore.Images.Media.insertImage(mContext.getContentResolver(), bitmap1, null, null));results=new Uri[]{result};}}uploadMessageAboveL.onReceiveValue(results);uploadMessageAboveL = null;}/*** 根据图片路径获取图p片* @param srcPath* @return*/private Bitmap getimage(String srcPath) {BitmapFactory.Options newOpts = new BitmapFactory.Options();// 开始读入图片,此时把options.inJustDecodeBounds 设回true了newOpts.inJustDecodeBounds = true;Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此时返回bm为空newOpts.inJustDecodeBounds = false;int w = newOpts.outWidth;int h = newOpts.outHeight;// 现在主流手机比较多是800*480分辨率,所以高和宽我们设置为float hh = 800f;// 这里设置高度为800ffloat ww = 480f;// 这里设置宽度为480f// 缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可int be = 1;// be=1表示不缩放if (w > h && w > ww) {// 如果宽度大的话根据宽度固定大小缩放be = (int) (newOpts.outWidth / ww);} else if (w < h && h > hh) {// 如果高度高的话根据宽度固定大小缩放be = (int) (newOpts.outHeight / hh);}if (be <= 0)be = 1;newOpts.inSampleSize = be;// 设置缩放比例// 重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了bitmap = BitmapFactory.decodeFile(srcPath, newOpts);return compressImage(bitmap);// 压缩好比例大小后再进行质量压缩}/*** 裁剪图片大小* @param image* @return*/private Bitmap compressImage(Bitmap image) {ByteArrayOutputStream baos = new ByteArrayOutputStream();image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中int options = 100;while (baos.toByteArray().length / 1024 > 100) { // 循环判断如果压缩后图片是否大于100kb,大于继续压缩baos.reset();// 重置baos即清空baosimage.compress(Bitmap.CompressFormat.JPEG, options, baos);// 这里压缩options%,把压缩后的数据存放到baos中options -= 10;// 每次都减少10}ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把压缩后的数据baos存放到ByteArrayInputStream中Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream数据生成图片return bitmap;}
}
在webview的界面,需要重写onActivityResult方法,并调用处理选择图片返回的方法。
protected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);Log.e(TAG,"收到返回消息了");mMyWebChromeClient.onActivityResult(requestCode,resultCode,data);}
添加上这些东西,上传头像基本搞定。
忘了说了,适配6.0的时候,还需要进行一下权限申请,拍照属于危险权限。
/*** 6.0 手机系统以上 检查并请求权限* @param context 必须为 Activity*/public static void checkAndRequestPermissionAbove23(Activity context){int i = ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA);if (i!= PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(context,new String[]{Manifest.permission.CAMERA},Constant.TAKE_PHOTO_PERMISSION);}}
到这里,基本就适配了所有版本,亲测可用。
Android WebView嵌入H5之file头像上传文件,适配所有版本相关推荐
- php 下 html5 XHR2 + FormData + File API 上传文件
FormData的作用: FormData对象可以帮助我们自动的打包表单数据,通过XMLHttpRequest的send()方法来提交表单.当然FormData也可以动态的append数据.FormD ...
- android 华为裁剪全图,华为手机头像上传裁剪操作 报错
华为手机头像上传操作, 裁剪点存储--进入裁剪后--点确定, 就直接报错退出程序了,其他机型测都是成功的,求大神指教 报错代码 @Override protected void onActivityR ...
- DISCUZ论坛插件h5手机电脑头像上传3.7.1带扩展插件【收集免费分享】
一个支持电脑和手机h5技术头像上传的插件. 说明:本插件h5电脑版和h5手机版为自主全新开发的触屏版头像上传,体验好,性能好,绿色. 主要特点:支持H5电脑版和H5手机版头像上传. 支持鼠标和触屏操作 ...
- springboot 头像上传 文件流保存 文件流返回浏览器查看 区分操作系统 windows 7 or linux...
1 //我的会员中心 头像上传接口 2 /*windows 调试*/ 3 @Value("${appImg.location}") 4 private String winPath ...
- 点击头像上传文件的效果
原理:label里的for和input里的id一样的话,点击label就等于点击input,这样把默认头像的图片放到label里,并把label的for和input(type:file)里的id设置成 ...
- ie8及其以下版本兼容性问题之input file隐藏上传文件
文件上传时,默认的file标签很难看,而且每个浏览器下都有很大差距.因此我们基本都把真正的file标签给隐藏,然后创建一个标签来替代它.但是由于IE出于安全方面的考虑上传文件时必须点击file的浏览按 ...
- input[type=file] 异步上传文件
背景 UI如图所示,其中有一个拍照图标,点击后要选择拍照或者从相册中选择要上传的图片. 拍照上传部分的代码如下 html部分 <div class="take-photo"& ...
- SSH Secure File Transfer上传文件错误:encountered 1 errors during the transfer解决办法
在使用SSH 工具向Linux服务器上传文件时,弹出 encountered 1 errors during the transfer 错误. 解决方案: 1.准备上传的那个文件所在目录路径存在(), ...
- angular文件上传php,Angular2里获取(input file)上传文件的内容的方法
最近在用Angular2,需要有一个上传文件的功能,但是又不想用库,所以直接用原生的input file 标签. 然后想获取上传的内容,于是先想了个愚蠢的方法,先通过id获取到这个input标签,然后 ...
最新文章
- 儿童学python编程入门用途-干货 | 看了此文,家长就知道为啥要让孩子学Python?...
- 从网页中读取数据 python_数据分析硬核技能:用 Python 爬取网页
- Vue状态管理vuex
- centos7下安装低版本mysql_centos7下使用yum安装制定版本mysql
- centos7 network网络服务重启报错failed to start lsb
- Gradle DSL method not found: ‘compile()’
- 敏捷系列情景剧 | 如何“正确”开每日站会
- 最新版python如何安装qt5_Python3 搭建Qt5 环境的方法示例
- Spark官方3 ---------Spark Streaming编程指南(1.5.0)
- 前端逼死强迫症系列之javascript
- openssl生成CA证书
- 常用电感封装与电流关系
- 单片机简易计算机设计实物,单片机设计的带余数的简易计算器
- 开发板、单片机、ARM的区别与对比
- Deepin 20社区版设置双屏显示
- win10 激活工具 Re-LoaderByR@1n.exe
- 安全防御----防火墙
- 惠普打印机故障代码_HP打印机故障代码
- 移除挖矿程序过程记录
- 《Java SE实战指南》15-04:接口和抽象类的区别