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
相关产品推荐
相关产品推荐

