You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 of file://, which Android blocks between apps for security reasons. This eliminates the URI exposure that caused the ERR_ACCESS_DENIED error.
  • 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 getExternalFilesDir means 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 04:12:38