Android Q: получить изображение из галереи и обработать его

Я знаю, что это очень простой вопрос, но он специально для Android Q.

Я просто хочу получить изображение из галереи, сжать его и отправить на сервер. Но из-за Scoped Storage в 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 ответа

Решение

Следующие вещи, которые я пробовал:

Нет возможного надежного getImagePathFromUri() реализация.

В этой настраиваемой логике у меня есть метод обработки выборки и поворота изображения следующим образом:

Вам не нужен Fileв этой функции. В конце концов, ваш самый первый оператор в этой функции создаетUri От этого File. Итак, заменитеFile параметр с Uri что у вас есть, и пропустите Uri.fromFile() вызов.

как я могу использовать изображение из внешнего хранилища как файл в Android 10.

Вы не можете. И, как показано выше, вам это не нужно для того, что вы делаете.

Если вы оказались в ситуации, когда застряли в использовании какой-либо библиотеки или API, которые абсолютно точно должны иметьFile:

  • Откройте InputStream по содержанию, используя contentResolver.openInputStream(), как вы делаете сегодня
  • Скопируйте байты из этого InputStream некоторым FileOutputStream в файле, который вы можете читать / писать (например, getCacheDir() на Context)
  • Используйте свою копию с библиотекой или API, для которых требуется File

Создайте каталог для данных, которые будут храниться в 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;
}
Другие вопросы по тегам