Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >webview拉起拍照和录像的爬坑终结篇

webview拉起拍照和录像的爬坑终结篇

原创
作者头像
老码小张
修改于 2021-01-23 08:01:59
修改于 2021-01-23 08:01:59
4.2K10
代码可运行
举报
文章被收录于专栏:玩转全栈玩转全栈
运行总次数:0
代码可运行

对于iOS环境上,简单的两个配置就OK啦

即只需在配置里加上摄像头和麦克风的使用权限。具体做法是在App 的info.plist中加入:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
.NSMicrophoneUsageDescription
.NSCameraUsageDescription

就完事了!

对于Android环境,就会比较复杂一点点:

step1、我们需要实现一个自己的 WebChromeClient,其主要目的就是为了拦截FileChooser这个选择文件的动作:

这里,用户在h5上点击文件,我们以下环节实现的WebChromeClient中,基于不同Android的api版本中的回调函数会被触发:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class EssWebChromeClient extends WebChromeClient {

     private Activity mActivity;

    public EssWebChromeClient(Activity activity) {
        mActivity = activity;
    }
    ///省略部分代码
    // For Android >= 3.0
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
        EssH5Sdk.getInstance().recordVideoForApiBelow21(uploadMsg, acceptType, mActivity);
    }
    // For Android >= 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        EssH5Sdk.getInstance().recordVideoForApiBelow21(uploadMsg, acceptType, mActivity);
    }

    @Override
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
        if(EssH5Sdk.getInstance().recordVideoForApi21(webView, filePathCallback, mActivity,fileChooserParams)){
            return true;
        }else{
            return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
        }
    }
}

这里我们注意以下,openFileChooser函数中会有一个acceptType的参数;

这个参数实际上是对应我们H5那个input框中的accept属性,需要我们关注:

accept 属性是一个字符串,它定义了文件 input 应该接受的文件类型。表示在 file 类型的 <input> 元素中用户可以选择的文件类型。每个唯一文件类型说明符可以采用下列形式之一:

  • 一个以英文句号(".")开头的合法的不区分大小写的文件名扩展名。例如: .jpg.pdf 或 .doc
  • 一个不带扩展名的 MIME 类型字符串。
  • 字符串 audio/*, 表示“任何音频文件”。
  • 字符串 video/*,表示 “任何视频文件”。
  • 字符串 image/*,表示 “任何图片文件”。

这里还有一个属性值得我们去关注:

capture 属性是一个字符串,如果accept 属性指出了 input 是图片或者视频类型,则它指定了使用哪个摄像头去这些数据。

值 :user 表示应该使用前置摄像头和/或麦克风。

值: environment 表示应该使用后置摄像头和/或麦克风。

step2、好了,当用户点击选择文件时,已经触发了我们的WebChromeClient中的选择文件的回调,接下来,我们实现原生拉起的想起拍照或者是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  public void recordVideoForApiBelow21(ValueCallback<Uri> uploadCallback, String acceptType, Activity activity) {
        if("image/*".equals(acceptType)){
            setUploadMessage(uploadMsg);
            startCamera(activity);
        }else if ("video/*".equals(acceptType)) {
            setUploadCallback(uploadCallback);
            recordVideo(activity);
        }
   }
   
  @TargetApi(21)
  public boolean recordVideoForApi21(WebView webView, ValueCallback<Uri[]> filePathCallback, Activity activity, WebChromeClient.FileChooserParams fileChooserParams){
        String acceptType = fileChooserParams.getAcceptTypes()[0];
        if("image/*".equals(acceptType)){
            setUploadCallbackV21(filePathCallback);
            startCamera(activity);
            return true;
        }
        if ("video/*".equals(acceptType) ){ 
            setUploadCallbackV21(filePathCallback);
            recordVideo(activity);
            return true;
        }
        return false;
  }

这里的我们注意到两个版本的api其实对于回调的形式是有区别的,21以上是接受一个Uri[]的callback,而低于21是接收一个Url的callback,这里注意一下就好,然后,我们看startCamera和recordVideo具体如何实现:

这里不妨先看一个简单的,如何录制视频:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void recordVideo(Activity activity){
        try {
            Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.putExtra("android.intent.extras.CAMERA_FACING", 1); // 调用前置摄像头

            activity.startActivityForResult(intent, VIDEO_REQUEST);
        } catch (Exception e) {
            e.printStackTrace();
        }
}

录制视频比较简单,当然我配置了默认拉起前置摄像头,基于具体业务场景,比如做人脸识别,有时候还是有一定的帮助的。

那么,录制玩视频,这个startActivityForResult,就会有一个onActivityResult的回调,我们去取他的Intent data,那么结果并调用相应的callback,应该还记得上面设置的按个callback吧:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (requestCode == VIDEO_REQUEST) { //根据请求码判断返回的是否是h5刷脸结果
     Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
     Uri[] uris = result == null ? null : new Uri[]{result};
     if (mUploadCallbackAboveL != null) {
            mUploadCallbackV21.onReceiveValue(uris);
            setUploadCallbackAboveL(null);
     } else {
            mUploadMessage.onReceiveValue(result);
            setUploadMessage(null);
     }
}

所以,我们看到了,无非就是基于不同的api来掉用起回调函数。

所以,同样的到来,拍照也是这样一个套路:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
private void takeCamera(Activity activity) {
        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePictureIntent.putExtra("android.intent.extras.CAMERA_FACING", 0); // 调用后置摄像头
        //https://ptyagicodecamp.github.io/accessing-pictures-using-fileprovider.html
        if (takePictureIntent.resolveActivity(activity.getPackageManager()) != null) {
            File photoFile = null;
            try {
                photoFile = createImageFile(activity);
                takePictureIntent.putExtra("PhotoPath", mCameraFilePath);
            } catch (IOException ex) {
                Log.e("TAG", "Unable to create Image File", ex);
            }
            //适配7.0
            if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
                if (photoFile != null) {
                    Uri photoURI = FileProvider.getUriForFile(activity,
                             "com.tencent.xxx.fileprovider", photoFile);
                    takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                }
            } else {
                if (photoFile != null) {
                    mCameraFilePath = "file:" + photoFile.getAbsolutePath();
                    takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,
                            Uri.fromFile(photoFile));
                } else {
                    takePictureIntent = null;
                }
            }
        }
        activity.startActivityForResult(takePictureIntent, TAKE_PHOTO_REQUEST);

    }
    
    
    private File createImageFile(Activity activity) throws IOException {
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        File storageDir = activity.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File image = File.createTempFile(
                imageFileName,  /* 前缀 */
                ".jpg",         /* 后缀 */
                storageDir      /* 文件夹 */
        );
        mCameraFilePath = image.getAbsolutePath();
        return image;
    }

等等,这里需要注意的是,7.0之后,Android系统不允许已file:的方式暴露文件,需要使用FileProvider,所以,这里需要在AndroidManifest.xml配置文件中去什么一个provider:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.tencent.xxx.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

android:authorities的取值需要注意一致,不然getUriForFile肯定就是crash了,而且是一个JNI的crash,莫名其妙,让你定位问题都及其蛋疼。

file_path.xml的内容如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.tencent.xxx/files/Pictures" />
</paths>

因为我们拍照存储的临时文件,防止在相册中:Environment.DIRECTORY_PICTURES,所以这里的path就是这个,当然,这个path你断点调试一下,抓一下photoFile 这个变量的路径,自然就知道改填啥了。

ok,依然是到了我们的onActivityResult环节:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
       if (requestCode == TAKE_PHOTO_REQUEST){
            if ( resultCode != RESULT_OK){//用户取消,传回一个空
                if (mUploadCallbackAboveL != null) {
                    mUploadCallbackAboveL.onReceiveValue(null);
                    setUploadCallbackV21(null);
                } else if (mUploadMessage != null) {
                    mUploadMessage.onReceiveValue(null);
                    setUploadMessage(null);
                }
                return;
            }
            Uri result = (data == null) ? null : data.getData();

            if (result == null && hasFile(mCameraFilePath)) {
                result = Uri.fromFile(new File(mCameraFilePath));
            }
            Uri[] uris = result == null ? null : new Uri[]{result};
            if (mUploadCallbackAboveL != null) {
                mUploadCallbackAboveL.onReceiveValue(uris);
                setUploadCallbackV21(null);
            } else if (mUploadMessage != null) {
                mUploadMessage.onReceiveValue(result);
                setUploadMessage(null);
            }
        }

这里需要注意一下,无论用户取消还是最终选择了,这里的data始终是null,但是我们可以通过resultCode来区分是否用户取消,用户取消的话,回调函数传回一个null就OK啦。

以上,就是WebChromeClient的具体细节,实现好之后,我们需要和webview关联上:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mWebView.setWebChromeClient(new EssWebChromeClient(H5Activity.this));

至此,webview上实现h5拍照,和录像的功能就完成了。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
1 条评论
热度
最新
源码可以提供下吗?
源码可以提供下吗?
回复回复点赞举报
推荐阅读
编辑精选文章
换一批
Android开发笔记(一百六十六)H5通过WebView录像上传
前面的博文《Android开发笔记(一百五十二)H5通过WebView上传图片》介绍了如何拍照上传给网页,不料客户又要求再加个摄像上传给网页。既然如此,那么再探讨一下如何实现这个摄像上传的功能。 与拍照上传一样,摄像上传也要重写WebChromeClient的openFileChooser/onShowFileChooser方法,在这两个方法内部跳转到系统的摄像机页面,示例代码如下:
aqi00
2019/01/18
1.4K0
Android 使用腾讯X5内核, Webview浏览器拍照或从相册上传图片
最近在项目开发中,需要使用WebView上传文件。默认情况下情况下,使用Android的WebView是不能够支持上传文件的。经过查找资料,得知需要重新WebChromeClient,根据选择到的文件Uri,传给页面去上传就可以了。
开发者技术前线
2020/11/23
2.2K0
Android 使用腾讯X5内核, Webview浏览器拍照或从相册上传图片
WebView 的 input 上传照片的兼容问题
问题 前几天接到的一个需求,是关于第三方理财产品的 H5 上传照片问题。 对方说他们的新的需求,需要接入方配合上传资产照片的需求,测试之后发现我们这边的 app 端,IOS 端上传没有问题,而 Android 端则点击没有任何反应。 对方 H5 调用的方式是通过<input type='file' accept='image/*'/>的方式调用,本来以为这个问题很简单,就是 app 端没有设置相机权限,造成的点击无反应情况,而实际上加了之后发现,并非简单的权限问题。 解决问题 因为 Android 的版本
非著名程序员
2018/02/09
2.2K0
Webview与H5交互——支持Intput type=”file“属性
  利用原生加H5进行混合开发时,遇到问题:在H5利用Input type=“file” 调用android本地图库上传图片时,在普通浏览器可以执行,在Webview上出现了问题。是因为 android webview 由于考虑安全原因屏蔽了 <input type="file"/> 这个功能 。重写webview 的WebChromeClient可以解决。
饮水思源为名
2018/09/06
1K0
Android WebView 上传文件支持全解析
声明:原文地址:http://blog.isming.me/2015/12/21/android-webview-upload-file/,转载请注明出处。 默认情况下情况下,使用Android的WebView是不能够支持上传文件的。而这个,也是在我们的前端工程师告知之后才了解的。因为Android的每个版本WebView的实现有差异,因此需要对不同版本去适配。花了一点时间,参考别人的代码,这个问题已经解决,这里把我踩过的坑分享出来。 主要思路是重写WebChromeClient,然后在WebViewAct
非著名程序员
2018/02/02
64.5K0
Android开发笔记(一百五十二)H5通过WebView上传图片
上一篇文章介绍了WebView与JS之间的数据交互,其实就是把字符串传来传去,这对文本格式的信息传输来说倒还凑合,倘若要传输图片信息就不管用了。所以,要想让h5网页支持从手机上传图片,还得另外想办法,当然各版本的Android系统也都提供了相应的解决办法。在Android 4.*系统上面,开发者可以重写WebChromeClient的openFileChooser函数;在Android 5.0以上的系统,开发者可以重写WebChromeClient的onShowFileChooser函数。话虽如此,可实际编码的时候,会发现并不容易,因为不但要兼容各种版本的安卓系统,而且要考虑不同操作方式下面的处理步骤。 首先是Android不同系统的适配问题,对于4.*版本要重写openFileChooser方法,对于5.0以上版本要重写onShowFileChooser方法。另外注意二者的回调方式也不一样,4.*的回调参数类型是ValueCallback<Uri>,而5.0以上的回调参数类型是ValueCallback<Uri[]>,因此要声明两个回调参数变量,分别用来保存二者各自的回调信息。相关代码如下所示:
aqi00
2019/01/18
1.4K0
让 Android 的 WebView 支持 type 为 file 的 input,同时支持拍照
Android 的 WebView 组件默认是不启用 type 为 file 的 input 的,需要在代码中做一些类似 hack 的编码(因为解决问题的目标对象的方法都是加了@hide注解的)才能召唤神龙。
LeoXu
2018/08/15
1.7K0
Android WebView那些坑之上传文件
最近公司项目需要在WebView上调用手机系统相册来上传图片,开发过程中发现在很多机器上无法正常唤起系统相册来选择图片。 解决问题之前我们先来说说WebView上传文件的逻辑:当我们在Web页面上点击选择文件的控件(<input type="file">)时,会回调WebChromeClient下的openFileChooser()(5.0及以上系统回调onShowFileChooser())。这个时候我们在openFileChooser方法中通过Intent打开系统相册或者支持该Intent的第三方
张磊BARON
2018/04/13
2.8K0
Android WebView那些坑之上传文件
android系统webview最新版本_webview加载h5页面空白
做android聊天时,遇到过一个问题,h5的页面发送的图片在android端不能响应,ios那边一路畅通。也是相当无奈,目前发现了好多android端与ios端webView的异同。
全栈程序员站长
2022/11/09
1.3K0
助你快速搭建一个健壮可控的WebApp
  笔者因公司需求,从0打造一款WebApp,一直维护到现在。整个接口算是从混乱到现在的有序。笔者也从一个WebView+H5的小菜鸟,磨炼成了中等生。   WebApp简单来讲,就是利用原生的WebView承载H5的html页面,并且实现JS和原生之间的通信。   WebApp的好处是显而易见的。业务页面来源于H5,原生作为一个承载壳提供流畅性支持,能够低成本的实现跨平台的实施以及快速嵌入微信小程序、钉钉、OA等APP中。与纯H5的App相比较,它能够更轻易的使用原生底层库,并且更加流畅;而与纯原生
饮水思源为名
2018/12/13
1.1K0
助你快速搭建一个健壮可控的WebApp
项目需求讨论 - WebView下拍照及图片选择功能
现在很多app里面,都会有这么一个需求,就是上传图片的按钮,当然按了这个按钮之后,就会出现二种选择: 1. 直接拍照,2. 相册选择现有图片。
青蛙要fly
2018/08/29
2.1K0
项目需求讨论 - WebView下拍照及图片选择功能
首个hybird商业项目踩坑总结
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列。该系列引用了《Android开发艺术探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相关知识,另外也借鉴了其他的优质博客,在此向各位大神表示感谢,膜拜!!!
LoveWFan
2018/09/27
1.3K0
首个hybird商业项目踩坑总结
WebView深度学习(二)之全面总结WebView遇到的坑及优化
这篇文章讲一下WebView遇到的那些坑,带领各位爬坑。这里如果有你没遇到的问题,欢迎留言告诉我,我尽我所能帮你解决。感谢大家支持。
AWeiLoveAndroid
2018/09/03
6.1K0
WebView深度学习(二)之全面总结WebView遇到的坑及优化
Android WebView选择图片、发送图片
主要代码来自:http://blog.csdn.net/woshinia/article/details/19030437 有删改
yechaoa
2022/06/10
9710
强大灵活的WebView代理库-PrimWeb
PrimWeb 是一个代理的WebView基于的 Android WebView 和 腾讯 x5 WebView,容易、灵活使用以及功能非常强大的库,提供了 WebView 一系列的问题解决方案 ,并且轻量和灵活, 更方便 webview 的切换.
用户3045442
2018/09/11
2.1K0
强大灵活的WebView代理库-PrimWeb
js与android webview交互
0x01 js调用java代码 android webview中支持通过添加js接口 webview.addJavascriptInterface(new JsInteration(), "control"); 参数说明: 第一个:java对象对应这个WebView的JavaScript上下文 第二个:调用java对象的js中引用对象 Parameters:  1 object the Java object to inject into this WebView's JavaScript context.
用户1148881
2018/01/17
4.2K0
FileProvider 的使用(Failed to find configured root that contains/storage/emulated/0/DCIM/ )
Add on 2020-9-24: 可以参考源码 CameraDemo 的file_provider分支上的代码。
全栈程序员站长
2022/11/16
1.8K0
FileProvider 的使用(Failed to find configured root that contains/storage/emulated/0/DCIM/ )
相关推荐
Android开发笔记(一百六十六)H5通过WebView录像上传
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验