Android Q:从图库中获取图像并进行处理

Par*_*war 6 android storage-access-framework android-10.0 scoped-storage

我知道这似乎是一个非常基本的问题,但它专门针对 Android Q。

我只想从图库中获取图像并将其压缩并发送到服务器。但是由于 Android Q 的 Scoped Storage,它比我想象的要难。我将首先解释我用代码做了什么:

首先,我发出选择图像的意图。

fun openGallery(fragment: Fragment){
    val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
    intent.type = "*/*"
    val mimeTypes = arrayOf("image/*")
    intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes)
    fragment.startActivityForResult(intent, REQUEST_IMAGE_PICK)
}
Run Code Online (Sandbox Code Playgroud)

它工作正常,我能够在 onActivityResult 方法中获取图像

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    if (requestCode == REQUEST_IMAGE_PICK && resultCode == Activity.RESULT_OK && null != data) {
        val selectedImage = data.data

        val source = ImageDecoder.createSource(activity!!.contentResolver, selectedImage)
        val bitmap = ImageDecoder.decodeBitmap(source)
        mBinding.circularProfileImage.setImageBitmap(bitmap)
    }
}
Run Code Online (Sandbox Code Playgroud)

好的,现在的问题是如何以文件格式访问此图像,以便我可以进一步处理/压缩它。

以下是我尝试过的事情:

val mImagePath = getImagePathFromUri(activity!!, selectedImage)
Run Code Online (Sandbox Code Playgroud)

这是我得到的路径:

/storage/emulated/0/DCIM/Camera/IMG_20191022_152437.jpg
Run Code Online (Sandbox Code Playgroud)

我通过以下方式从中创建了一个文件:

val file = File(mImagePath)
Run Code Online (Sandbox Code Playgroud)

以下是我压缩和上传图像的自定义逻辑:

val mNewFile = MediaCompressor.compressCapturedImage(activity!!, file, "ProfilePictures")
uploadProfile(mNewFile)
Run Code Online (Sandbox Code Playgroud)

在这个自定义逻辑中,我有一个方法来处理图像的采样和旋转,如下所示:

fun handleSamplingAndRotationBitmap(context: Context, selectedImage: File, reqWidth: Int, reqHeight: Int): Bitmap {

    val mUri = Uri.fromFile(selectedImage)

    // First decode with inJustDecodeBounds=true to check dimensions
    val options = BitmapFactory.Options()
    options.inJustDecodeBounds = true
    var imageStream = context.contentResolver.openInputStream(mUri)
    BitmapFactory.decodeStream(imageStream, null, options)
    imageStream!!.close()

    // Calculate inSampleSize
    options.inSampleSize =
        calculateInSampleSize(options, reqWidth, reqHeight)

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false
    imageStream = context.contentResolver.openInputStream(mUri)

    var img = BitmapFactory.decodeStream(imageStream, null, options)

    img = rotateImageIfRequired(context, img!!, mUri)
    return img
}
Run Code Online (Sandbox Code Playgroud)

但是当我尝试使用context.contentResolver.openInputStream打开流时, 我收到以下错误:

java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20191022_152437.jpg: open failed: EACCES (Permission denied)
Run Code Online (Sandbox Code Playgroud)

我知道我收到此信息是因为在 Android 10 中我们无权直接访问外部存储中的文件。

所以,请帮我解决这个问题,如何将外部存储中的图像用作 Android 10 中的文件。

注意:我拥有所有必需的权限,所以这不是问题

Com*_*are 5

以下是我尝试过的事情:

没有可能的可靠getImagePathFromUri()实现。

在这个自定义逻辑中,我有一个方法来处理图像的采样和旋转,如下所示:

您不需要File该功能中的 a 。毕竟,您在该函数中的第一个语句Uri从该File. 因此,将File参数替换为Uri您拥有的参数,并跳过Uri.fromFile()调用。

如何将外部存储中的图像用作 Android 10 中的文件。

你不能。而且,如上所示,您在执行的操作中不需要它。

如果您发现自己在使用某些绝对肯定必须具有以下功能的库或 API 时陷入困境File

  • 打开InputStream内容,使用contentResolver.openInputStream(),就像您今天所做的那样
  • 将其中的字节复制InputStreamFileOutputStream您可以读/写的文件上的某个(例如,getCacheDir()on Context
  • 将您的副本与需要 File