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

Android WebView相机拍照上传无响应,无法加载图片至输入框

Android WebView相机拍照上传无响应问题修复

问题描述

开发Android WebView网页应用时,相机拍照后点击发送无响应,无法将图片加载到输入框。排查发现相机拍照时response.getData().getDataString()返回空值,但文件管理器上传功能正常。

问题原因

  1. 使用MediaStore.ACTION_IMAGE_CAPTURE并指定MediaStore.EXTRA_OUTPUT时,相机不会通过response.getData()返回Uri,而是直接将图片写入你指定的文件路径,因此response.getData()为空是正常行为。
  2. Android 7.0+禁止直接使用file://Uri暴露文件,必须通过FileProvider生成content://格式的Uri,否则会触发安全异常,同时WebView也无法正常访问file://Uri。

解决方案

1. 修复相机Intent的Uri生成逻辑

在onShowFileChooser方法中,替换Uri.fromFile()为FileProvider生成content://Uri,并添加临时权限:

//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(MainActivity.this.getPackageManager()) != null){
        File photoFile = null;
        try{
            photoFile = createImageFile();
        }catch(IOException ex){
            Log.e(TAG, "Image file creation failed", ex);
        }
        if(photoFile != null){
            // 使用FileProvider生成content格式的Uri
            mCM = FileProvider.getUriForFile(MainActivity.this, 
                    getPackageName(), photoFile).toString();
            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(mCM));
            // 授予临时读取权限
            takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }else{
            takePictureIntent = null;
        }
    }
    // 其余代码保持不变
}

2. 优化图片存储路径(Android 10+适配)

修改createImageFile方法,使用应用私有存储目录,避免存储权限问题:

// Create an image file
private File createImageFile() throws IOException{
    @SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "img_"+timeStamp+"_";
    // 使用应用私有图片目录,Android 10+无需WRITE_EXTERNAL_STORAGE权限
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File tempFile = File.createTempFile(imageFileName,".jpg",storageDir);
    return tempFile;
}

3. 配置FileProvider路径

在res/xml目录下创建provider_paths.xml,配置私有存储路径的访问权限:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- 对应getExternalFilesDir(Environment.DIRECTORY_PICTURES)的路径 -->
    <external-files-path name="pictures" path="Pictures"/>
</paths>

4. 调整Manifest权限

针对Android版本优化权限声明,避免不必要的权限请求:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<!-- Android 10+无需读写外部存储权限,限制到SDK 28 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28"/>
<uses-permission android:name="android.permission.INTERNET"/>
<!-- Android 13+图片权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>

5. 确认ActivityResult逻辑

原代码中onActivityResult的空值判断逻辑正确,无需修改,确保当response.getData()为空时,直接使用预先生成的mCM Uri:

if (response.getData() == null) {
    if (mCM != null) {
        results = new Uri[]{Uri.parse(mCM)};
    }
}

修改后的完整MainActivity.java关键代码

// 省略import部分...
public class MainActivity extends AppCompatActivity {
private WebView webView;
PermissionManager permissionManager;

    private static final String TAG = MainActivity.class.getSimpleName();
    private String mCM;
    private ValueCallback<Uri> mUM;
    private ValueCallback<Uri[]> mUMA;
    
    private ActivityResultLauncher<Intent> imgFinished  = registerForActivityResult(
            new ActivityResultContracts.StartActivityForResult(),
            new ActivityResultCallback<ActivityResult>() {
                @Override
                public void onActivityResult(ActivityResult response) {
                    if (Build.VERSION.SDK_INT >= 21) {
                        Uri[] results = null;
                        if (response.getResultCode() == Activity.RESULT_OK) {
                            if (null == mUMA) {
                                return;
                            }
                            if (response.getData() == null) {
                                if (mCM != null) {
                                    results = new Uri[]{Uri.parse(mCM)};
                                }
                            } else {
                                String dataString = response.getData().getDataString();
                                if (dataString != null) {
                                    results = new Uri[]{Uri.parse(dataString)};
                                }
                            }
                        }
                        mUMA.onReceiveValue(results);
                        mUMA = null;
                    } else {
                        if (null == mUM) return;
                        Uri result = response == null || response.getResultCode() != RESULT_OK ? null : response.getData().getData();
                        mUM.onReceiveValue(result);
                        mUM = null;
                    }
                }
            }
    );
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        webView = findViewById(R.id.webView);
        WebSettings MyWebviewSettings = webView.getSettings();
        MyWebviewSettings.setAllowFileAccessFromFileURLs(true);
        MyWebviewSettings.setAllowUniversalAccessFromFileURLs(true);
        MyWebviewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        MyWebviewSettings.setJavaScriptEnabled(true);
        MyWebviewSettings.setDomStorageEnabled(true);
        MyWebviewSettings.setBuiltInZoomControls(true);
        MyWebviewSettings.setAllowFileAccess(true);
        MyWebviewSettings.setSupportZoom(true);
        MyWebviewSettings.setAllowContentAccess(true);
        MyWebviewSettings.setMediaPlaybackRequiresUserGesture(false);
        webView.loadUrl("https://hbwfc-acc.9knots.nl/app");
        webView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onPermissionRequest(final PermissionRequest request) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    request.grant(request.getResources());
                }
            }
    
            //For Android 3.0+
            public void openFileChooser(ValueCallback<Uri> uploadMsg){
                mUM = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                imgFinished.launch(Intent.createChooser(i,"File Chooser"));
            }
            // For Android 3.0+
            public void openFileChooser(ValueCallback uploadMsg, String acceptType){
                mUM = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                imgFinished.launch(Intent.createChooser(i, "File Browser"));
            }
            //For Android 4.1+
            public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture){
                mUM = uploadMsg;
                Intent i = new Intent(Intent.ACTION_GET_CONTENT);
                i.addCategory(Intent.CATEGORY_OPENABLE);
                i.setType("*/*");
                imgFinished.launch(Intent.createChooser(i, "File Chooser"));
            }
            //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(MainActivity.this.getPackageManager()) != null){
                    File photoFile = null;
                    try{
                        photoFile = createImageFile();
                    }catch(IOException ex){
                        Log.e(TAG, "Image file creation failed", ex);
                    }
                    if(photoFile != null){
                        mCM = FileProvider.getUriForFile(MainActivity.this, 
                                getPackageName(), photoFile).toString();
                        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.parse(mCM));
                        takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }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, "Image Chooser");
                chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, intentArray);
                imgFinished.launch(chooserIntent);
                return true;
            }
        });
        webView.setWebViewClient(new WebViewClient());
        webView.setWebContentsDebuggingEnabled(true);
        permissionManager = new PermissionManager() {};
        permissionManager.checkAndRequestPermissions(this);
    }
    
    // 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 = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File tempFile = File.createTempFile(imageFileName,".jpg",storageDir);
        return tempFile;
    }
}

内容的提问来源于stack exchange,提问作者Gerben Mol

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 03:45:53