最近发现公司APP内嵌的网页点击上传图片没任何效果,查资料得知是Android安全限制,WebView内网页默认没有读取本地文件的权限。虽然网上有解决方案,但是由于Android版本几经波折,API签名大相径庭,想要完全兼容,要实现多个方法,为方便以后使用,简单记录一下。
首先,在使用WebView的页面,增加如下全局变量,方便回调使用:
private static final String FILE_CHOOSER = "选择操作"; private String mCM; private ValueCallback<Uri> mUM; private ValueCallback<Uri[]> mUMA; private final static int FCR = 1;
然后覆盖onActivityResult方法,内部实现如下:
//解决android input[type=file] 无法选择图片 @Override protected void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if (Build.VERSION.SDK_INT >= 21) { Uri[] results = null; //Check if response is positive if (resultCode == Activity.RESULT_OK) { if (requestCode == FCR) { if (null == mUMA) { return; } if (intent == null) { //Capture Photo if no image available if (mCM != null) { results = new Uri[]{Uri.parse(mCM)}; } } else { String dataString = intent.getDataString(); if (dataString != null) { results = new Uri[]{Uri.parse(dataString)}; } } } } mUMA.onReceiveValue(results); mUMA = null; } else { if (requestCode == FCR) { if (null == mUM) return; Uri result = intent == null || resultCode != RESULT_OK ? null : intent.getData(); mUM.onReceiveValue(result); mUM = null; } } }
关键部分,继承WebChromeClient,并实现如下方法,最终调用WebView的setWebChromeClient方法:
//For Android 3.0+ public void openFileChooser(ValueCallback<Uri> uploadMsg) { mUM = uploadMsg; Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); startActivityForResult(Intent.createChooser(intent, FILE_CHOOSER), FCR); } // For Android 3.0+, above method not supported in some android 3+ versions, in such case we use this public void openFileChooser(ValueCallback uploadMsg, String acceptType) { mUM = uploadMsg; Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); startActivityForResult(Intent.createChooser(intent, FILE_CHOOSER), FCR); } //For Android 4.1+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) { mUM = uploadMsg; Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); startActivityForResult(Intent.createChooser(intent, FILE_CHOOSER), FCR); } //For Android 5.0+ public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { if (mUMA != null) { mUMA.onReceiveValue(null); } mUMA = filePathCallback; Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { photoFile = createImageFile(); takePictureIntent.putExtra("PhotoPath", mCM); } catch (IOException ex) { Log.e(TAG, "Image file creation failed", ex); } if (photoFile != null) { mCM = "file:" + photoFile.getAbsolutePath(); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile)); } else { takePictureIntent = null; } } Intent contentSelectionIntent = new Intent(Intent.ACTION_GET_CONTENT); contentSelectionIntent.addCategory(Intent.CATEGORY_OPENABLE); contentSelectionIntent.setType("*/*"); Intent[] intentArray; if (takePictureIntent != null) { intentArray = new Intent[]{takePictureIntent}; } else { intentArray = new Intent[0]; } Intent chooserIntent = new Intent(Intent.ACTION_CHOOSER); chooserIntent.putExtra(Intent.EXTRA_INTENT, contentSelectionIntent); chooserIntent.putExtra(Intent.EXTRA_TITLE, FILE_CHOOSER); chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray); startActivityForResult(chooserIntent, FCR); return true; } // Create an image file private File createImageFile() throws IOException { @SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); String imageFileName = "img_" + timeStamp + "_"; File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); return File.createTempFile(imageFileName, ".jpg", storageDir); }
注:最后一步四个主要方法中,startActivityForResult实为WebView所在Activity的方法,这里因为我自己的WebView做了定制,实现直接写在Activity,并且定义了接口传入自定义的WebView内,这样分离了WebView与Activity的代码,如果你WebView没有做定制等,这里可以改为YourActivity.this.startActivityForResult,请悉知。
上一篇: Android软键盘遮挡输入框问题解决 下一篇: 为你的Express应用增加CSRF防护