Android通过WebView选择文件上传(支持直接调起相机等应用)

前言
最近项目中上的一个banner,里面有个需求,需要在网页中调用系统的拍照功能或者选择文件,获取图片在网页显示出来,并上传到客户的服务器。但是在banner上线后,发现我们的app支持的Webview中不支持这个功能,而把链接复制到系统自带的或者一些第三方浏览器中都可以进行拍照/文件管理器选择,所以我知道这肯定是通用的js,而我们app做的支持不够了。接着,我决定找找原因。
一番查找之后,我发现这个功能,简单而言,其实就是在一个html页面中有这样一段类似的代码

<input class="filePrew" type="file" capture="camera" accept="image/*" name="image">

,我们要在 webview 中添加对这一类 js 的支持。
实现
通过我大Stack Overflow的反馈,我采用了实现这种功能的目前的一种通用做法:
第一步:自定义webview的WebChromeClient,在其中覆盖掉openFileChooser方法。

注意:这里是WebChromeClient,不是WebViewClient。而且openFileChooser方法在WebChromeClient中有@hide标记,不过只管重写即可。

贴上代码:

/** 选择文件进行上传 */
            // Andorid 4.1+
            public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
                openFileChooser(uploadFile, acceptType);
            }

            // Andorid 3.0 +
            public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType) {
                mUploadFile = uploadFile;
                act.startActivityForResult(createDefaultOpenableIntent(), REQUEST_UPLOAD_FILE_CODE);
            }

            // Android 3.0
            public void openFileChooser(ValueCallback<Uri> uploadFile) {
                openFileChooser(uploadFile, "");
            }

上面的createDefaultOpenableIntent()方法,我借鉴了源码com.android.browser.UploadHandler,下面也把相关代码给出:

/** 默认支持上传多种文件类型 */
    private Intent createDefaultOpenableIntent() {
        // Create and return a chooser with the default OPENABLE
        // actions including the camera, camcorder and sound
        // recorder where available.
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("*/*");

        Intent camcorderIntent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
        Intent soundRecorderIntent = new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);

        Intent chooser = createChooserIntent(createCameraIntent(), soundRecorderIntent);
        chooser.putExtra(Intent.EXTRA_INTENT, i);
        chooser.putExtra(Intent.EXTRA_TITLE, "请选择要上传的文件");
        return chooser;
    }

    private Intent createCameraIntent() {
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        File externalDataDir = Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM);
        File cameraDataDir = new File(externalDataDir.getAbsolutePath() +
                File.separator + "browser-photos");
        cameraDataDir.mkdirs();
        mCameraFilePath = cameraDataDir.getAbsolutePath() + File.separator +
                System.currentTimeMillis() + ".jpg";
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));
        return cameraIntent;
    }

    private Intent createChooserIntent(Intent... intents) {
        Intent chooser = new Intent(Intent.ACTION_CHOOSER);
        chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, intents);
        return chooser;
    }

注意:在创建 chooser 时,想要同时令多种类型的 intent 生效,要先把这些 intent 存放在Intents[]中,再调用putExtra(Intent.EXTRA_INITIAL_INTENTS, intents)方法把它们添加到 chooser 中;而不是调用“方法一个一个地添加,这样得到的 chooser 里面会只有一个额外 intent 生效。

第二步:在Activity的onActivityResult方法中进行处理,获取对应文件。
代码继续奉上:

if (requestCode == REQUEST_UPLOAD_FILE_CODE) {
            if (resultCode == RESULT_OK) {
                // 获取需上传文件成功
                if (null == mUploadFile) {
                    return;
                }
                Uri result = (null == data) ? null : data.getData();
                if (result == null) {
                    // 从相机获取
                    File cameraFile = new File(mCameraFilePath);
                    if (cameraFile.exists()) {
                        result = Uri.fromFile(cameraFile);
                        // 扫描相册
                        act.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
                    }
                }
                try {
                    if (mUploadFile != null) {
                        mUploadFile.onReceiveValue(result);
                        mUploadFile = null;
                    }
                } catch (Exception e) {
                    MeilaLog.e(TAG, e);
                }
            } else {
                // 获取需上传文件失败或者取消操作
                if (mUploadFile != null) {
                    mUploadFile.onReceiveValue(null);
                    mUploadFile = null;
                }
            }
        }

这里也有一点需要注意的是,不管请求是否成功,在这个回调里面我们都要执行mUploadFile.onReceiveValue(null);,否则,h5 中的那个 js 在执行一次后,将不再起作用。

最后,上面代码用到的相关变量:

    private ValueCallback<Uri> mUploadFile;
    private String mCameraFilePath;
    private static final int REQUEST_UPLOAD_FILE_CODE = 1003; // 选择文件请求码

总结

这样就可以实现在WebView中上传文件了,当然还要记得要给Activity添加相应的权限~
经手头上的机子测试验证,上面的代码处理,已经可以支持以下这几种机型(系统):小米、华为、魅族、vivo、oppo……排名不分先后,哈哈~
已知问题:在锤子系统上,会出现选择完图片返回 webview 无法正常读取图片的问题(图片位置显示空白),但是通过调起相机拍摄的照片是可以正常读取到(当然了,相机我做了单独处理啊,妈蛋>_<)。
由于项目中是对全文件类型支持,所以不好单独对返回的uri进行相关图片处理,后续会加上js发起的文件类型的判断,也就是前面openFileChooser方法中的acceptType参数,到时候再尝试对这个地方针对image类型做处理。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现网页通过 WebView 调用 Android 的图片或文件选择,可以使用以下步骤: 1.在 Android 代码中,为 WebView 设置 WebChromeClient,并重写 onShowFileChooser 方法。 2.在 onShowFileChooser 方法中,调用系统的文件选择器或图库,让用户选择要上传的文件或图片。 3.将用户选择文件或图片返回给 WebView,并在 JavaScript 中处理上传操作。 下面是实现步骤的示例代码: 1.在 Android 代码中,为 WebView 设置 WebChromeClient,并重写 onShowFileChooser 方法。 ```java webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { // 在这里调用系统的文件选择器或图库,让用户选择要上传的文件或图片 return true; } }); ``` 2.在 onShowFileChooser 方法中,调用系统的文件选择器或图库,让用户选择要上传的文件或图片。 ```java @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { Intent intent = fileChooserParams.createIntent(); try { startActivityForResult(intent, REQUEST_CODE_UPLOAD_FILE); } catch (ActivityNotFoundException e) { e.printStackTrace(); } } return true; } ``` 3.将用户选择文件或图片返回给 WebView,并在 JavaScript 中处理上传操作。 ```java @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_UPLOAD_FILE && resultCode == RESULT_OK) { Uri[] uris = new Uri[1]; uris[0] = data.getData(); if (mFilePathCallback != null) { mFilePathCallback.onReceiveValue(uris); } } } ``` 在 JavaScript 中,可以通过监听 input[type=file] 的 change 事件,获取用户选择文件或图片。 ```javascript var fileInput = document.createElement('input'); fileInput.type = 'file'; fileInput.onchange = function() { var file = fileInput.files[0]; // 处理上传操作 }; fileInput.click(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值