首页 > 解决方案 > 从图库和相机上传 Android Webview 图像,相机不工作

问题描述

我一直在这里搜索有关此问题的所有相关帖子,但我无法弄清楚为什么从我的相机拍照时图像上传不起作用(在 Android 8 设备上测试)。图库上传按预期工作正常。

在活动结果中,我得到了缓存目录中图像的路径(变量 mCameraPhotoPath),但似乎结果或网站无法访问该文件,或者那里可能有一个空的图像文件。

直接在设备上通过谷歌浏览器拍摄和上传图像是可行的,所以我认为问题不在于网站方面。

这是我的权限

<uses-feature android:name="android.hardware.camera"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.CAMERA"/>
<!-- for new versions api 21+-->
<uses-permission
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="18" />
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CAMERA2" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />

这是我的应用程序定义

 <application
    android:name="***"
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:requestLegacyExternalStorage="true"
    android:hardwareAccelerated="true"
    android:theme="@style/Theme.Android">

    <!-- added fileprovider support -->
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>

然后我在 res 目录中添加了一个 xml 文件夹,其中包含一个名为 file_path.xml 的文件并添加了内容

<paths>
  <cache-path
    name="mediaimages"
    path="./">
  </cache-path>
</paths>

这是我的活动代码

private var mUploadCallbackAboveL: ValueCallback<Array<Uri>>? = null
private var mUploadCallbackBelow: ValueCallback<Uri?>? = null
private var imageUri: Uri? = null

override fun onCreate(savedInstanceState: Bundle?) {   
  super.onCreate(savedInstanceState)
  // webview definition 
  myWebView.webChromeClient = initChromeWebViewClient()
  myWebView.webViewClient = initWebViewClient()
  myWebView.settings.javaScriptEnabled = true
  myWebView.settings.domStorageEnabled = true
  myWebView.settings.allowFileAccess = true
  myWebView.settings.allowContentAccess = true
  myWebView.settings.supportZoom()
  myWebView.visibility = View.INVISIBLE
  myWebView.settings.userAgentString = "android"
  myWebView.setLayerType(View.LAYER_TYPE_HARDWARE, null)

  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      myWebView.settings.mixedContentMode = 0
  }
}

private fun initChromeWebViewClient() : WebChromeClient {
    webChromeViewClient = object : WebChromeClient() {
        /**
         * API > = 21 (Android 5.0.1) calls back this method
         */
        override fun onShowFileChooser(
            webView: WebView?,
            valueCallback: ValueCallback<Array<Uri>>,
            fileChooserParams: FileChooserParams?
        ): Boolean {
            //(1) when the method calls back, it indicates that the version API > = 21. In this case, assign the result to muploadcallbackabovel to make it! = null
            mUploadCallbackAboveL = valueCallback
            takePhoto()
            return true
        }
    }
    return webChromeViewClient
}

@Override
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_CODE) {
        val file: File = File(imageUri!!.toString())
        val file_size = (file.length() / 1024).toString().toInt()
        //After the above two assignment operations (1) and (2), we can decide which processing method to adopt according to whether its value is empty
        if (mUploadCallbackBelow != null) {
            chooseBelow(resultCode, data);
        } else if (mUploadCallbackAboveL != null) {
            chooseAbove(resultCode, data);
        } else {
            Toast.makeText (this, "an error occurred.", Toast.LENGTH_SHORT).show();
        }
    }
}

创建和启动选择器意图的方法

private fun takePhoto() {
    //Adjust the camera in a way that specifies the storage location for taking pictures
    var photoFile : File? = null
    val authorities : String = applicationContext.packageName + ".provider"
    try {
        photoFile = createImageFile()
        imageUri = FileProvider.getUriForFile(this, authorities, photoFile)
    } catch(e: IOException) {
        e.printStackTrace()
    }

    val captureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri)
    val Photo = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    val chooserIntent = Intent.createChooser(Photo, "Image Chooser")
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf<Parcelable>(captureIntent))
    startActivityForResult(chooserIntent, REQUEST_CODE)
}

@Throws(IOException::class)
  private fun createImageFile(): File {
    val imageFileName = "JPEG_" + SimpleDateFormat("yyyyMMdd_HHmmss").format(Date())
    return File(applicationContext.cacheDir.toString() + File.separator + imageFileName + ".jpg")
}

处理 Android L+ 结果的方法

private fun chooseAbove(resultCode : Int, data : Intent?) {
    if (RESULT_OK == resultCode) {
        updatePhotos()

        if (data != null) {
            //Here is the processing of selecting pictures from a file
            val results : Array<Uri>
            val uriData : Uri? = data.data
            if (uriData != null) {
                results = arrayOf(uriData)
                mUploadCallbackAboveL!!.onReceiveValue(results)
            } else {
                mUploadCallbackAboveL!!.onReceiveValue(null)
            }
        } else {
            if (imageUri != null) {
                mUploadCallbackAboveL!!.onReceiveValue(arrayOf(imageUri!!))
            }
        }
    } else {
        mUploadCallbackAboveL!!.onReceiveValue(null)
    }
    mUploadCallbackAboveL = null
}

编辑:将代码更新为工作代码,直到 Android 10,Android 11 不起作用。图片已拍摄,但未显示在 Webviews 输入中。似乎 Fileprovider 方法不起作用或为 Android 11 配置错误。

标签: androidfile-uploadcameraandroid-webview

解决方案


问题已更新为工作代码。现在选择器意图允许从图库中选择图像或由 Android +5 设备的相机拍摄。

主要问题是,没有使用 FileProvider,并且在 file_paths.xml 文件中未正确指定缓存目录。

感谢@blackapps 的帮助并指导正确的方向!


推荐阅读