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

Android WebView实现相册/相机选图上传功能求助

解决WebView中图片上传无法唤起相册/相机的问题

WebView默认不会处理文件选择交互,要实现和移动端浏览器一致的效果,需要完成以下几步配置:

1. 添加必要权限与FileProvider配置

在AndroidManifest.xml中添加存储、相机权限,并配置FileProvider适配Android 7.0+的文件Uri规范:

<!-- 存储权限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
<!-- 相机权限(支持拍照上传) -->
<uses-permission android:name="android.permission.CAMERA" />

<application ...>
    <!-- FileProvider配置 -->
    <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>
</application>

在res/xml目录下新建file_paths.xml文件(无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" />
    <external-cache-path name="my_cache" path="Pictures" />
</paths>

2. 修改MainActivity代码

通过设置WebChromeClient拦截文件选择请求,同时处理动态权限申请和选择结果回调:

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

public class MainActivity extends AppCompatActivity {
    private WebView webView;
    private ValueCallback<Uri[]> filePathCallback;
    private Uri photoUri;
    private static final int REQUEST_PERMISSIONS = 100;
    private static final int REQUEST_FILE_CHOOSER = 101;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        webView = findViewById(R.id.webview);
        webView.setWebViewClient(new MyWebView());
        
        // 设置WebChromeClient处理文件选择逻辑
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> callback, FileChooserParams params) {
                filePathCallback = callback;
                // Android6.0+先检查权限
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
                            || checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.CAMERA},
                                REQUEST_PERMISSIONS);
                        return true;
                    }
                }
                // 权限已获取,唤起选择器
                openFileChooser();
                return true;
            }
        });

        WebSettings webSettings = webView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setAllowFileAccess(true);
        webSettings.setAllowContentAccess(true);
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);

        webView.loadUrl("https://www.mysitenamegoeshear.com/");
    }

    // 打开相册+相机选择器
    private void openFileChooser() {
        // 相册选择Intent
        Intent galleryIntent = new Intent(Intent.ACTION_GET_CONTENT);
        galleryIntent.setType("image/*");
        galleryIntent.addCategory(Intent.CATEGORY_OPENABLE);

        // 拍照Intent
        Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (cameraIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
                String imageFileName = "JPEG_" + timeStamp + "_";
                File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
                photoFile = File.createTempFile(imageFileName, ".jpg", storageDir);
                photoUri = FileProvider.getUriForFile(this, getPackageName() + ".fileprovider", photoFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (photoFile != null) {
                cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            }
        }

        // 组合选择器
        Intent chooserIntent = Intent.createChooser(galleryIntent, "选择图片");
        if (cameraIntent.resolveActivity(getPackageManager()) != null) {
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{cameraIntent});
        }
        startActivityForResult(chooserIntent, REQUEST_FILE_CHOOSER);
    }

    // 权限申请回调
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_PERMISSIONS) {
            boolean allGranted = true;
            for (int result : grantResults) {
                if (result != PackageManager.PERMISSION_GRANTED) {
                    allGranted = false;
                    break;
                }
            }
            if (allGranted) {
                openFileChooser();
            }
        }
    }

    // 文件选择结果回调
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_FILE_CHOOSER && filePathCallback != null) {
            Uri[] results = null;
            // 处理拍照结果
            if (resultCode == RESULT_OK && photoUri != null) {
                results = new Uri[]{photoUri};
            }
            // 处理相册选择结果
            if (data != null && data.getData() != null) {
                results = new Uri[]{data.getData()};
            }
            // 将结果返回给WebView
            filePathCallback.onReceiveValue(results);
            filePathCallback = null;
            photoUri = null;
        }
    }

    private class MyWebView extends WebViewClient {
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
            return super.shouldOverrideUrlLoading(view, request);
        }
    }

    @Override
    public void onBackPressed() {
        if (webView.isFocused() && webView.canGoBack()) {
            webView.goBack();
        } else {
            super.onBackPressed();
        }
    }
}

核心逻辑说明

  • WebChromeClient:必须通过该类的onShowFileChooser方法拦截WebView的文件选择请求,这是实现网页文件上传的核心入口。
  • 动态权限:Android6.0+需主动申请存储、相机权限,否则无法访问相册或调用相机。
  • FileProvider:Android7.0+禁止直接使用file://Uri,需通过FileProvider生成content://Uri避免权限异常。
  • 结果回调:选择图片后,需将Uri数组通过filePathCallback.onReceiveValue返回给WebView,网页才能获取到选中的文件。

内容的提问来源于stack exchange,提问作者Dev Beginner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 06:35:43