WebView上传文件遇到的坑openFileChooser

最近公司项目碰到WebView上传图片的问题,在这方面纠结了挺长时间,把解决问题的思路记录一下。

至于 WebView 需要的配置方面这里就不说了,自行百度。

首先 WebView 要重写 WebViewClient 的 shouldOverrideUrlLoading 方法,让链接直接
在APP内部打开而不是跳转到系统浏览器或者是第三方浏览器:

 webview.setWebViewClient(new WebViewClient(){
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return super.shouldOverrideUrlLoading(view, url);
    }
});

上面这个还不是遇到的问题,真正的问题就在于在 WebView 的链接里通过按钮打开手机相册,
但是安卓这么多版本又不会自动兼容,这就需要我们自己去做兼容。上网了解到在安卓5.0版本
以后SDK中开放的方法是 onShowFileChooser ,而在较低版本中是 openFileChooser 。然后在
API21(由于项目引用23的SDK,此方法被隐藏,不太清楚是不是21)之后 openFileCHooser 就
被 Google 隐藏掉了。

/**
     * Tell the client to open a file chooser.
     * @param uploadFile A ValueCallback to set the URI of the file to upload.
     *      onReceiveValue must be called to wake up the thread.a
     * @param acceptType The value of the 'accept' attribute of the input tag
     *         associated with this file picker.
     * @param capture The value of the 'capture' attribute of the input tag
     *         associated with this file picker.
     *
     * @deprecated Use {@link #showFileChooser} instead.
     * @hide This method was not published in any SDK version.
     */
    @SystemApi
    @Deprecated
    public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
        uploadFile.onReceiveValue(null);
    }

从上面代码可以看出此方法被隐藏了,所以正常情况下这个方法是无法调用的。
但是如果非要用被隐藏的方法,那被隐藏的方法怎么才能引用呢?

第一种方法 可以利用反射机制。《这种方法自行百度搜索》

下面说说我在项目中用到的方法:

因为 openFileChooser 方法被隐藏 ,所以在 WebChromeClient 中重写的时候就没有此方法。
于是我便自己写一个类去继承 WebChromeClient 。在类中写兼容各个版本的 openFileChooser
方法。代码如下:

    private ValueCallback<Uri> mUploadMessage;
    private ValueCallback<Uri[]> uploadMessage; // 用于5.0以上
    private static final int FILECHOOSER_RESULTCODE = 1;
    public static final int REQUEST_SELECT_FILE = 100;


public class MyWebChromeClient extends WebChromeClient {
        /**
         * {@inheritDoc}
         * 安卓3.0-
         */
        public void openFileChooser(ValueCallback<Uri> uploadMsg) {
            openFileChooser(uploadMsg, "");
        }

        /**
         * {@inheritDoc}
         * 安卓3.0 - 4.0
         */
        public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
            mUploadMessage = uploadMsg;
            Intent i = null;
            if (Build.VERSION.SDK_INT < 19) {
                i = new Intent(Intent.ACTION_GET_CONTENT);
                i.setType("image/*");
            } else {
                i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
            }
            startActivityForResult(Intent.createChooser(i, "选择图片"), FILECHOOSER_RESULTCODE);
        }

        /**
         * {@inheritDoc}
         * 安卓 4.0 - 5.0
         */
        protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
            openFileChooser(uploadMsg, acceptType);
        }

        /**
        *  安卓5.0+
        */
        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
            if (uploadMessage != null) {
                uploadMessage.onReceiveValue(null);
                uploadMessage = null;
            }

            uploadMessage = filePathCallback;

            Intent intent = fileChooserParams.createIntent();
            try {
                startActivityForResult(Intent.createChooser(intent, "选择图片"), REQUEST_SELECT_FILE);
            } catch (ActivityNotFoundException e) {
                uploadMessage = null;
                Toast.makeText(getApplicationContext(), "文件上传失败", Toast.LENGTH_LONG).show();
                return false;
            }
            return true;
        }
    }

然后需要重写 onActivityResult 方法接收返回的数据即可:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (requestCode == REQUEST_SELECT_FILE) {
                if (uploadMessage == null)
                    return;
                Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
                if (result != null) {
                    uploadMessage.onReceiveValue(new Uri[]{result});
                } else {
                    uploadMessage.onReceiveValue(new Uri[]{});
                }
                uploadMessage = null; //每次置空是为了防止下次点击按钮选择图片无响应
            }
        } else if (requestCode == FILECHOOSER_RESULTCODE) {
            if (null == mUploadMessage)
                return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            mUploadMessage.onReceiveValue(result); //注意此处
            mUploadMessage = null;
        } else
            Toast.makeText(getApplicationContext(), "上传文件失败", Toast.LENGTH_LONG).show();

        return;
    }

到这里工作就完成了90%,什么?到这里还没完成?

是的,在部分机型上上传图片之后,但是在WebView中显示图片缩略图确实TXT文档类型。
这是为什么?我个人理解认为是5.0以下与5.0以上图片返回的Uri格式不一致造成的。

预览为TXT格式

一时间就把我难住了,尝试了各种方法,包括安卓不同版本系统打开图库的方式都无效。
突然大神附体似的就想到了会不会和返回 Uri 的格式不同有关?选择的图片肯定都是有
自己的路径的,我可不可以获取到图片的路径呢?

同时又发现有一个 Uri.fromFile() 的方法,而又可以将图片的路径转换成File类型。于是
拿起就是干马上尝试。

于是有了以下代码:

// 根据图片的Uri得到图片的路径
private String getImagePath(Uri originalUri) {
        String[] pro = {MediaStore.Images.Media.DATA};
        Cursor cursor = getContentResolver().query(originalUri, pro, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        cursor.moveToFirst();
        String path = cursor.getString(column_index);
        return path;
    }

然后在onActivityResult中调用,完整代码:

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (requestCode == REQUEST_SELECT_FILE) {
                if (uploadMessage == null)
                    return;
                Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
                if (result != null) {
                    uploadMessage.onReceiveValue(new Uri[]{result});
                } else {
                    uploadMessage.onReceiveValue(new Uri[]{});
                }
                uploadMessage = null;
            }
        } else if (requestCode == FILECHOOSER_RESULTCODE) {
            if (null == mUploadMessage)
                return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            Uri uri = Uri.fromFile(new File(getImagePath(result)));
            mUploadMessage.onReceiveValue(uri); // 转换后
            mUploadMessage = null;
        } else
            Toast.makeText(getApplicationContext(), "文件上传失败", Toast.LENGTH_LONG).show();

        return;
    }

正如上面,因为mUploadMessage.onReceiveValue(uri)中需要接收一个Uri类型的参数
因此又需要将图片路径转换成Uri。这样就奇迹般的起效了。然后就能正常的显示图片。

**

以上纯属个人理解,如有错误欢迎指出,一起共同学习。

**

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值