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

时间:2019-10-24 10:42:18

标签: android android-10.0 storage-access-framework scoped-storage

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

我只想从Gallery中获取图像并将其压缩并发送到服务器。但是由于Android 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中的文件。

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

2 个答案:

答案 0 :(得分:1)

  

以下是我尝试过的事情:

没有可靠的getImagePathFromUri()实现。

  

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

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

  

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

不能。而且,如上所述,您不需要在做任何事情。

如果您发现自己陷入了某种情况,那就是使用某些绝对肯定要具有File的库或API:

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

答案 1 :(得分:0)

为要存储在 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() 中:

@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;
}