android - 从图库和相机上传 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 配置错误。
解决方案
问题已更新为工作代码。现在选择器意图允许从图库中选择图像或由 Android +5 设备的相机拍摄。
主要问题是,没有使用 FileProvider,并且在 file_paths.xml 文件中未正确指定缓存目录。
感谢@blackapps 的帮助并指导正确的方向!
推荐阅读
- c++ - C++ 模板类 vtable 函数突然不再在共享库中生成
- c# - 在 .net 核心中使用 BuildAction 为“内容”的 .resx 资源文件进行本地化
- php - Laravel - 显示名称而不是 id
- android - 深色主题不适用于某些 recyclerview 项目
- javascript - 未捕获的类型错误:无法读取 todolist 的属性
- java - log4j - 写入文件名作为记录器名称
- sql - MS SQL:创建 GROUP BY 语句
- reactjs - 如何从firestore获取数据并使用react js将其设置到输入框
- python - 如何使用 Python 查找当前用户的系统主题颜色?
- javascript - 单击提交按钮后如何根据文本值调整文本框的大小?