(转)Android5.0及以上系统WebView上传图片遇到的那些坑

原地址:Android5.0及以上系统WebView上传图片遇到的那些坑_fuzhongbin的博客-CSDN博客

之前项目中有做在WebView中上传文件的操作(调用系统相册选择照片来进行上传),当我们在web页面上点击选择文件的控件

(<input type="file">)时,会回調WebChromeClient下的openFileChoose();(5.0及以上的系统会掉的是onShowFileChooser())。这个

时候我们在这两个方法中通过Intent打开系统相册活着支持改Intent的第三方应用来选择图片。如下代码:
 

public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
    uploadMessage = valueCallback;
    openImageChooserActivity();
}
 
private void openImageChooserActivity() {
    Intent i = new Intent(Intent.ACTION_GET_CONTENT);
    i.addCategory(Intent.CATEGORY_OPENABLE);
    i.setType("image/*");
    startActivityForResult(Intent.createChooser(i, 
                "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
}

最后我们在onActivityForResult()中将选择的图片的内容通过ValueCallback的onReceiveValue方法返回给WebView,然后通过Js

上传。代码如下:、

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == FILE_CHOOSER_RESULT_CODE) {
        Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
        if (uploadMessage != null) {
            uploadMessage.onReceiveValue(result);
            uploadMessage = null;
        }
    }
}

VauleCallback是WebView组件通过openFileChooser()或者onShowFileChooser()提供给我们的,它里面包含一个或者一组
Uri,然后我们在onActivityResult()里将Uri传给ValueCallback的onReceiveValue()方法,这样WebView就知道我们选择了什么

文件。

很多5.0以上的系统使用openFileChooser()方法不能唤起系统相册或第三方app来选择图片,所以在5.0以上要重写

onShowFileChooser(),所以为了解决这个问题兼容各个版本,我们需要对openFileChooser()进行重载,同时针对5.0及以上系统

提供onShowFileChooser()方法:


 

webview.setWebChromeClient(new WebChromeClient() {
 
        // For Android < 3.0
        public void openFileChooser(ValueCallback<Uri> valueCallback) {
            ***
        }
 
        // For Android  >= 3.0
        public void openFileChooser(ValueCallback valueCallback, String acceptType) {
            ***
        }
 
        //For Android  >= 4.1
        public void openFileChooser(ValueCallback<Uri> valueCallback, 
                String acceptType, String capture) {
            ***
        }
 
        // For Android >= 5.0
        @Override
        public boolean onShowFileChooser(WebView webView, 
                ValueCallback<Uri[]> filePathCallback, 
                WebChromeClient.FileChooserParams fileChooserParams) {
            ***
            return true;
        }
    });

上述***代表你要打开系统相册并缓存valueCallback或filePathCallback;


大家应该注意到在onShowfileChooser()中的ValueCallback包含了一组Uri(Uri[]),所以针对5.0以上的系统我们还需要在

onActivityForResult()中做一点点处理,见下面完整代码。

处理完这个还有一步需要处理,那就是在混淆文件中混淆openFileChooser(),如果在混淆文件中不进行处理坑会导致打包好之后

的apk安装到真机上无法选择图片,解决方案如下:
 

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

下面贴出完整源码:

public class MainActivity extends AppCompatActivity {
 
    private ValueCallback<Uri> uploadMessage;
    private ValueCallback<Uri[]> uploadMessageAboveL;
    private final static int FILE_CHOOSER_RESULT_CODE = 10000;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        WebView webview = (WebView) findViewById(R.id.web_view);
        assert webview != null;
        WebSettings settings = webview.getSettings();
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setJavaScriptEnabled(true);
        webview.setWebChromeClient(new WebChromeClient() {
 
            // For Android < 3.0
            public void openFileChooser(ValueCallback<Uri> valueCallback) {
                uploadMessage = valueCallback;
                openImageChooserActivity();
            }
 
            // For Android  >= 3.0
            public void openFileChooser(ValueCallback valueCallback, String acceptType) {
                uploadMessage = valueCallback;
                openImageChooserActivity();
            }
 
            //For Android  >= 4.1
            public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) {
                uploadMessage = valueCallback;
                openImageChooserActivity();
            }
 
            // For Android >= 5.0
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
                uploadMessageAboveL = filePathCallback;
                openImageChooserActivity();
                return true;
            }
        });
        String targetUrl = "file:///android_asset/up.html";
        webview.loadUrl(targetUrl);
    }
 
    private void openImageChooserActivity() {
        Intent i = new Intent(Intent.ACTION_GET_CONTENT);
        i.addCategory(Intent.CATEGORY_OPENABLE);
        i.setType("image/*");
        startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE);
    }
 
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FILE_CHOOSER_RESULT_CODE) {
            if (null == uploadMessage && null == uploadMessageAboveL) return;
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            if (uploadMessageAboveL != null) {
                onActivityResultAboveL(requestCode, resultCode, data);
            } else if (uploadMessage != null) {
                uploadMessage.onReceiveValue(result);
                uploadMessage = null;
            }
        }
    }
 
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private void onActivityResultAboveL(int requestCode, int resultCode, Intent intent) {
        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)};
            }
        }
        uploadMessageAboveL.onReceiveValue(results);
        uploadMessageAboveL = null;
    }
}

注意事项:
做完这些可能调用系统相册是没有问题的,但是如果自己写的一个选择图片的页面,我们通常可能会在onActivityForResult()中
回传的是一个放置选中图片绝对路径的List<String> path=new List();这样的话可能在5.0及以上系统有些选择图片之后不能显

示,比如我亲测的有华为手机6.0,7.0系统,因为Android系统相册默认的回传路径是相对路径是可以正常上传,所以我猜测不能

显示的原因跟路径有关,这种情况下我们需要在onActivityForResult()中对5.0及以上的系统处理代码中将绝对路径转换为相对路

径,绝对路径转相对路径的的代码如下:
 

public static Uri getImageContentUri(Context context, String filePath) {//File imageFile
        //String filePath = imageFile.getAbsolutePath();//根据文件来获取路径
        Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ",
                new String[] { filePath }, null);
        if (cursor != null && cursor.moveToFirst()) {
            int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID));
            Uri baseUri = Uri.parse("content://media/external/images/media");
            return Uri.withAppendedPath(baseUri, "" + id);
        } else {
            if (StringUtil.isNotEmpty(filePath)) {//imageFile.exists()判断文件存不存在
                ContentValues values = new ContentValues();
                values.put(MediaStore.Images.Media.DATA, filePath);
                return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
            } else {
                return null;
            }
        }
    }

本人建议在onActivityForResult()方法中可以将imageFile传递回来便于处理,传递绝对路径的话就将绝对路径转换为相对路径然
后通过ValueCallback(results)将路径传递进去即可。

如果上传之后在华为6。0,7.0系统中不能显示,首先要确保有下面这两个设置:
 

        mWebview.getSettings().setJavaScriptEnabled(true);//启用js
        mWebview.getSettings().setBlockNetworkImage(false);//解决图片不显示

如果这两个都有设置,就可能是你用https获取的图片引用的是http的,因为Android WebView从Lollipop开始默认不允许混合模式,https当中不能加载http资源,需要设置开启。

则需要在WebSetting中添加如下代码进行设置:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){//
            webSettings.setMixedContentMode(android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值