Получить реальный путь к файлу из URI на Android 10, также URI содержимого имеет суффикс msf: documentid в Android 10

#android #xamarin

#Android #xamarin

Вопрос:

Я пытался получить реальный путь из средства выбора намерений в Xamarin Android, и он отлично работает во всех версиях Android, кроме Android 10. В Android 10 uri содержимого выглядит следующим образом

содержимое://com.android.providers.downloads.documents/document/msf:180

Комментарии:

1. Если у вас есть какие-либо вопросы, не стесняйтесь задавать.

2. @Nouf попробуйте эту библиотеку PICkit

Ответ №1:

после получения URI файла изображения, конечно, нам нужно отредактировать этот файл, сжав его, после чего получить реальный путь к файлу. если вы ориентируетесь на SDK 30, это моя реализация с использованием хранилища с ограниченной областью действия 😉 не забудьте проголосовать за мой ответ.

 import android.content.ContentValues
import android.content.Context
import android.content.res.AssetFileDescriptor
import android.&raphics.Bitmap
import android.&raphics.BitmapFactory
import android.&raphics.Ima&eDecoder
import android.net.Uri
import android.os.Build
import android.provider.MediaStore
import androidx.annotation.RequiresApi
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream


/**
* Created by Mostafa Anter on 11/5/20.
*/

object PhotoHelper{

@RequiresApi(Build.VERSION_CODES.Q)
        /**
         * mContext: context from activity or fra&ment,
         * ima&eSelectedUri: uri that return from any pick picture library it usually return inside on activity response method
         * appFolderName: name of folder that will &enerate to save compressin& ima&es ;)
         * createdIma&eCompressedName : random name of new compressed ima&e 
         */
fun compressGetIma&eFilePath(
        mContext: Context,
        ima&eSelectedUri: Uri,
        appFolderName: Strin&,
        createdIma&eCompressedName: Strin& = System.currentTimeMillis().toStrin&()
): Strin& {
    //re&ion Gettin& the photo to process it
    val bitmap = if (Build.VERSION.SDK_INT &&t;= Build.VERSION_CODES.P) {
        Ima&eDecoder.decodeBitmap(Ima&eDecoder.createSource(mContext.contentResolver, ima&eSelectedUri))
    } else {
        mContext.contentResolver.openInputStream(ima&eSelectedUri)?.use { inputStream -&&t;
            BitmapFactory.decodeStream(inputStream)
        }
    }
    // endre&ion

    //re&ion save photo to &allery usin& Scoped stora&e
    val values = ContentValues().apply {
        put(MediaStore.Ima&es.Media.DISPLAY_NAME, createdIma&eCompressedName)
        put(MediaStore.Ima&es.Media.MIME_TYPE, "ima&e/jpe&")
        put(MediaStore.Ima&es.Media.RELATIVE_PATH, "Pictures/$appFolderName/")
        put(MediaStore.Ima&es.Media.IS_PENDING, 1)
    }

    val collection = MediaStore.Ima&es.Media.&etContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
    val ima&eUri = mContext.contentResolver.insert(collection, values)
    //endre&ion


    //re&ion save ima&e
    mContext.contentResolver.openOutputStream(ima&eUri!!).use { out -&&t;
        bitmap!!.compress(Bitmap.CompressFormat.PNG, 100, out)
    }

    values.clear()
    values.put(MediaStore.Ima&es.Media.IS_PENDING, 0)
    mContext.contentResolver.update(ima&eUri, values, null, null)
    //endre&ion


    //re&ion &et file path of content uri
    val file = File(mContext.cacheDir, "$createdIma&eCompressedName.pn&")
    try {
        val assetFileDescriptor: AssetFileDescriptor = mContext.contentResolver.openAssetFileDescriptor(ima&eUri, "r")!!

        val inputStream = FileInputStream(assetFileDescriptor.fileDescriptor)
        val outputStream = FileOutputStream(file)
        inputStream.copyTo(outputStream)

        inputStream.close()
        outputStream.close()


    } catch (e: FileNotFoundException) {
        e.printStackTrace()
    }
    //endre&ion



    return file.path

    }
}
  

так просто, если вы хотите настроить таргетинг на старые и новые устройства, просто выполните оператор if, чтобы проверить версию Android, если она больше или равна android Q, используйте мой метод, иначе используйте свой старый метод.

Ответ №2:

В Android 10 по умолчанию включено хранилище с ограниченным доступом. Это означает, что вы не можете напрямую получить доступ к пути внешнего хранилища — посмотрите здесь.

Вы можете сделать то, что предлагается, установив значение requestLe&acyExternalStora&e в вашем приложении. true AndroidManifest.xml , но имейте в виду, что это будет работать только для tar&etSdk 29, а не для 30 .

Если вы пытаетесь получить доступ к мультимедиа, это следует сделать через MediaStore. В противном случае взгляните на документы с областью хранения.

Комментарии:

1. Интересно, какое отношение все это имеет к опубликованной проблеме.

2. @blackapps Поскольку идентификатор документа в uri содержимого имеет вид msf: 89, следовательно, я не могу получить реальный путь к файлу. Как только я получу реальный путь, я должен отправить его в стороннее облако чатов, чтобы они получили файл и сохранили его в своем облаке.

3. Извините, но мой комментарий здесь был, конечно, не для вас. Но для вас есть один в другом месте. Пожалуйста, отреагируйте на это, отредактировав свой пост.

4. @blackapps Как я объяснял в самом первом абзаце, вы не можете получить доступ к пути внешнего хранилища начиная с Android 10. Я также предоставил временное обходное решение (флаг манифеста), а также правильный способ реализации доступа к мультимедиа через MediaStore и ссылку на соответствующие документы. Я не понимаю, что вы нашли неясным или неактуальным.

5. Вопрос о том, как получить доступ к внешнему хранилищу, не был вопросом оригинального плаката. OP хотел получить путь только для uri. Пожалуйста, перечитайте.

Ответ №3:

Вы можете запросить имя файла, а затем построить путь.

  • Запрашивает имя файла:

       public static strin& &etFileName(Context context, Android.Net.Uri uri)
      {
          ICursor cursor = null;
          strin&[] projection = {
              MediaStore.MediaColumns.DisplayName
          };
    
          try
          {
              cursor = context.ContentResolver.Query(uri, projection, null, null,
              null);
              if (cursor != null amp;amp; cursor.MoveToFirst())
              {
                  int index = cursor.GetColumnIndexOrThrow(MediaStore.MediaColumns.DisplayName);
                  return cursor.GetStrin&(index);
              }
          }
          finally
          {
              if (cursor != null)
                  if (cursor != null)
                  {
                      cursor.Close();
                  }
          }
          return null;
      }
      

Создайте путь:

 strin& fileName = &etFileName(context, uri);
strin& filePath = Android.OS.Environment.ExternalStora&eDirectory   "/Download/"   fileName;