Android WebView无法上传相机拍摄图片(可正常上传图库图片)
Hey there! That net::ERR_ACCESS_DENIED error when uploading camera-captured images in your WebView app is super common, and it almost always ties back to file URI exposure issues (especially on Android 7.0+) or missing runtime permissions. Let’s fix this step by step:
1. Fix File URI Exposure with FileProvider (Android 7.0+)
Starting from Android 7.0 (API 24), apps can’t directly share file:// URIs with other apps (like the camera) — this triggers a FileUriExposedException, which leads to the access denied error. We need to use FileProvider to generate a secure content:// URI instead.
Step 1: Create a File Paths Configuration
Create a new directory res/xml in your project, then add a file named file_paths.xml:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-files-path name="my_images" path="Pictures/DirectoryNameHere" /> <external-public-path name="public_images" path="Pictures/DirectoryNameHere" /> </paths>
This tells FileProvider where to locate your captured images.
Step 2: Register FileProvider in AndroidManifest.xml
Add this inside your <application> tag:
<provider android:name="androidx.core.content.FileProvider" android:authorities="${applicationId}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
The ${applicationId} will automatically use your app’s package name, so no hardcoding needed.
Step 3: Update Camera Intent & File Creation Code
Modify the createImageFile method to use app-specific storage (avoids permission headaches on newer Android versions):
private File createImageFile() throws IOException { // Use app-specific external storage instead of public directories File imageStorageDir = new File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), "DirectoryNameHere"); if (!imageStorageDir.exists()) { imageStorageDir.mkdirs(); } return File.createTempFile( "IMG_" + System.currentTimeMillis(), /* File prefix */ ".jpg", /* File suffix */ imageStorageDir /* Storage directory */ ); }
Then update the camera intent setup in onShowFileChooser to use FileProvider:
if (takePictureIntent.resolveActivity(getPackageManager()) != null) { File photoFile = null; try { photoFile = createImageFile(); mCameraPhotoPath = photoFile.getAbsolutePath(); } catch (IOException ex) { Log.e(TAG, "Unable to create Image File", ex); } if (photoFile != null) { // Generate secure content URI via FileProvider Uri photoURI = FileProvider.getUriForFile( this, getPackageName() + ".fileprovider", photoFile ); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); // Grant temporary permission to the camera app to access the file takePictureIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); } else { takePictureIntent = null; } }
2. Add Runtime Permission Checks (Android 6.0+)
Even with static permissions in AndroidManifest.xml, you need to request permissions at runtime for Android 6.0 (API 23) and above. Add this to your onCreate method:
// Check and request required permissions String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED || checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { requestPermissions(permissions, 100); } }
And add the permission request callback:
@Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 100) { boolean allGranted = true; for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { allGranted = false; break; } } if (!allGranted) { Toast.makeText(this, "Permissions are required to use camera and upload images", Toast.LENGTH_SHORT).show(); } } }
Note: For Android 13 (API 33+), replace WRITE_EXTERNAL_STORAGE with READ_MEDIA_IMAGES since storage permissions were updated.
3. Fix a Small Typo in Your Code
I noticed you have ValueCallback<uri> (lowercase uri) in a few places — this should be ValueCallback<Uri> (uppercase Uri) to avoid compilation errors. Correct instances like:
private ValueCallback<Uri> mUploadMessage; // was uri before
Why This Works
- FileProvider: It securely shares files using
content://URIs instead offile://, which Android blocks between apps for security reasons. This eliminates the URI exposure that caused theERR_ACCESS_DENIEDerror. - Runtime Permissions: Ensures your app actually has the permissions it needs to access the camera and storage when running on newer Android versions.
- App-Specific Storage: Using
getExternalFilesDirmeans you don’t need extra permissions for Android 10+, since this directory is private to your app.
After making these changes, your camera upload should work without the access denied error!
内容的提问来源于stack exchange,提问作者vignesh ds

