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

Android 安卓Q:从图库中获取图像并进行处理,android,android-10.0,storage-access-framework,scoped-storage,Android,Android 10.0,Storage Access Framework,Scoped Storage,我知道这似乎是一个非常基本的问题,但这是专门针对安卓Q的 我只想从Gallery中获取一张图像,并将其压缩并发送到服务器。但由于安卓Q的范围存储,这比我想象的要难。我将首先解释我对代码做了什么: 首先,我发出了选择图像的意图 fun openGallery(fragment: Fragment){ val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) intent

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

我只想从Gallery中获取一张图像,并将其压缩并发送到服务器。但由于安卓Q的范围存储,这比我想象的要难。我将首先解释我对代码做了什么:

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

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)
}
它工作得很好,我能够用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)
    }
}
好的,现在的问题是如何以文件格式访问此图像,以便进一步处理/压缩它

我试过以下几件事:

val mImagePath = getImagePathFromUri(activity!!, selectedImage)
这是我得到的路径:

/storage/emulated/0/DCIM/Camera/IMG_20191022_152437.jpg
我用以下方式从中创建了一个文件:

val file = File(mImagePath)
下面是我压缩和上传图像的自定义逻辑:

val mNewFile = MediaCompressor.compressCapturedImage(activity!!, file, "ProfilePictures")
uploadProfile(mNewFile)
在这个自定义逻辑中,我有一个方法来处理图像的采样和旋转,如下所示:

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
}
但是当我试图使用context.contentResolver.openInputStream打开流时 我得到以下错误:

java.io.FileNotFoundException: /storage/emulated/0/DCIM/Camera/IMG_20191022_152437.jpg: open failed: EACCES (Permission denied)
我知道我得到了这个,因为在Android 10中,我们没有直接从外部存储访问文件的权限

所以,请帮我弄清楚,我如何在Android 10中使用外部存储器中的图像作为文件

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

我试过以下几件事:

val mImagePath = getImagePathFromUri(activity!!, selectedImage)
没有可靠的
getImagePathFromUri()
实现

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

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
}
在该函数中不需要
文件
。毕竟,该函数中的第一条语句将从该
文件创建一个
Uri
。因此,用您拥有的
Uri
替换
File
参数,并跳过
Uri.fromFile()
调用

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

你不能。而且,如上所示,您在做什么时不需要它

如果您发现自己在某些情况下无法使用某些库或API,而这些库或API必须有一个
文件

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

通过以下方式为要存储在Android/data/package name中的数据创建目录:

private void createDir() {
    String timeStamp = utils.currentTimeStamp();
    File storageDir = getExternalFilesDir(null);
    File image;
    try {
        image = File.createTempFile(timeStamp, ".png", storageDir);
        Log.i("SANJAY ", "createDir: " + image.getPath());
    } catch (IOException e) {
        e.printStackTrace();
        Log.i("SANJAY ", "createDir: " + e.getMessage());

    }
}
现在调用图库意图:

 intent = new Intent();
 intent.setAction(Intent.ACTION_GET_CONTENT);
 intent.addCategory(Intent.CATEGORY_OPENABLE);
 intent.setType("image/*");

 startActivityForResult(intent, 100);
在onActivityResult()中:

用户可以使用以下方法从文件中获取Uri:

private File saveBitMap(Context context, Bitmap Final_bitmap) {
    File pictureFileDir = new File(Environment.getExternalStorageDirectory()
            + "/Android/data/"
            + getApplicationContext().getPackageName()
            + "/"/*Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), ""*/);
    if (!pictureFileDir.exists()) {
        boolean isDirectoryCreated = pictureFileDir.mkdirs();
        if (!isDirectoryCreated)
            Log.i("SANJAY ", "Can't create directory to save the image");
        return null;
    }
    String filename = pictureFileDir.getPath() + File.separator + System.currentTimeMillis() + ".jpg";
    File pictureFile = new File(filename);
    try {
        pictureFile.createNewFile();
        FileOutputStream oStream = new FileOutputStream(pictureFile);
        Final_bitmap.compress(Bitmap.CompressFormat.PNG, 18, oStream);
        oStream.flush();
        oStream.close();
        Log.i("SANJAY ", "saveBitMap :: Save Image Successfully..");

    } catch (IOException e) {
        e.printStackTrace();
        Log.i("SANJAY", "There was an issue saving the image.");
        Log.i("SANJAY", "Error :: " + e.getLocalizedMessage());
    }
    return pictureFile;
}

现在的问题是如何以文件格式访问此图像,以便进一步处理/压缩它。
您甚至不会尝试以文件格式获取路径。没有必要,因为您可以通过在获取的uri上打开inputstream来完成所有处理。getContextResolver().openInputStream(data.data);谢谢你,@blackapps。如果图像是一个视频,而我只想像以前一样用第三方视频播放器播放,那会怎么样?我曾经用默认的android视频播放器通过传递文件来播放视频。你能分享工作代码吗@ParagPawaries,它起作用了,真是个愚蠢的错误。非常感谢你们,也感谢你们提供的额外建议。再问一个问题,这在10岁以下的Android上有效吗?还是我必须使用旧的API为相同的问题编写一个替代解决方案?@parapawar:我不确定“this”指的是什么。正如我所指出的,
getImagePathFromUri()
通常不可靠。我对
MediaCompressor
了解不多。但是,如果您需要的话,您的
handleSamplingAndRotationBitmap()
函数并没有绑定到Android 10。请注意,您有
是代码气味——这些函数可以合法地返回
null
,您应该优雅地处理这些场景。@FaiqMustaqeem:我不确定“它”在“它工作吗”中指的是什么。如果你在回答的底部引用了我的建议,那么是的,它是有效的。@FaiqMustaqeem:是的,这种技术将适用于所有支持
文件提供者的Android版本(API级别15及更高的IIRC)。缺点是拷贝所需的时间和存储空间。