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

7

我知道这似乎是一个非常基本的问题,但它专门针对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)
}

这很好,我能在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中将外部存储器中的图像作为文件使用。

注意:我已经获得了所有必要的权限,所以这不是问题所在。


3
现在的问题是如何以文件格式访问这张图片,以便能够进一步处理/压缩它。您甚至不需要获取文件格式的路径,因为您可以通过在获得的URI上打开输入流来进行所有处理。 - blackapps
3
getContextResolver().openInputStream(data.data); 可以翻译为:getContextResolver().openInputStream(data.data); - blackapps
1
谢谢你,@blackapps。它正在运行。 - Parag Pawar
如果这个文件是一个视频,而我只想像以前那样使用第三方视频播放器来流式传输它,该怎么办呢?以前我只需传递文件就可以使用默认的 Android 视频播放器进行视频流。 - TatiOverflow
你能分享一下可用的代码吗?@ParagPawar - Umeshkumar D.
2个回答

6
以下是我尝试过的方法:
在可靠的 `getImagePathFromUri()` 实现中不存在。
在这个自定义逻辑中,我有一个处理图片采样和旋转的方法,如下所示:
在该函数中不需要一个 `File`。毕竟,在该函数的最开始,您会从那个 `File` 创建一个 `Uri`。因此,用您有的 `Uri` 替换 `File` 参数,并跳过调用 `Uri.fromFile()`。
如何在 Android 10 中将外部存储的图像用作文件?
你不能。正如上面所示,对于你正在做的事情,你不需要它。
如果你发现自己陷入某种情况,必须使用一些绝对必须有一个 `File` 的库或 API:
- 打开一个 `InputStream`,使用 `contentResolver.openInputStream()` 在内容上。 - 将这个 `InputStream` 的字节复制到可以读/写的某个文件的 `FileOutputStream` 上(例如,在 `Context` 上使用 `getCacheDir()` )。 - 使用需要 `File` 的库或 API 进行复制。

是的,它正在工作,这是一个愚蠢的错误。非常感谢您,还要感谢您提供的额外指针。 - Parag Pawar
我正在使用Unity视频播放器,但出现了“content://”协议不受支持的问题。libcurl中未支持或禁用了“content”协议。 - TatiOverflow
该死。我一直在为Android设备-28工作,现在看来我只能用更困难的方式来完成它了。将视频内容复制到缓存目录中。这当然会使存储空间变得臃肿。但我想这是唯一的选择。 - TatiOverflow
1
@FaiqMustaqeem:我不确定“它”在“它是否有效”中指的是什么。如果您指的是我的答案底部的建议,那么是的,它有效。 - CommonsWare
1
@FaiqMustaqeem:是的,该技术将适用于支持“FileProvider”(API级别15及更高版本)的所有Android版本。缺点是需要复制所需的时间和存储空间。 - CommonsWare
显示剩余7条评论

0

创建一个目录,用于在Android/data/包名中存储数据:

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()方法中:

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == RESULT_OK) {
        if (requestCode == 100) {
           Uri mediaUri = data.getData();
            
            //display the image
            try {
                InputStream inputStream = getBaseContext().getContentResolver().openInputStream(mediaUri);
                Bitmap bm = BitmapFactory.decodeStream(inputStream);

                ByteArrayOutputStream stream = new ByteArrayOutputStream();
                byte[] byteArray = stream.toByteArray();

                bind.photo.setImageBitmap(bm);
                //Log.i("SANJAY ", "onActivityResult: " + saveBitMap(this, bm));
                uri = Uri.fromFile(saveBitMap(this, bm));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }

        } 
    }
}

使用此方法从文件获取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;
}

所以,您是说打开图库的意图调用在新版本中保持不变? - Vivek Thummar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接