Получить PDF из MediaStore после получения содержимого: // Uri в onActivityResult?

Я запускаю намерение ACTION_GET_CONTENT, чтобы выбрать PDF:

override fun routeToFilePicker() {
    val intent = Intent()
    intent.type = MediaType.PDF.toString()
    intent.action = Intent.ACTION_GET_CONTENT
    activity.startActivityForResult(
        Intent.createChooser(intent, "Select PDF"),
        REQUEST_CODE_PDF_PICKER
    )
}

Затем onActivityResult я пытаюсь создать PDF-файл из Uri (content//:path):

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_PDF_PICKER ) {
        data?.data?.let { pdfUri: Uri ->
            val pdfFile: File = pdfUri.toFile() <-- chrash
            ...
        }
    }
}

pdfUri.toFile() вызывает фатальное исключение:

java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1003, result=-1, data=Intent { dat=content://com.android.providers.downloads.documents/document/3569 flg=0x1 }} to activity {my.package.name.activity}: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569

Caused by: java.lang.IllegalArgumentException: Uri lacks 'file' scheme: content://com.android.providers.downloads.documents/document/3569

Мне нужен файл, чтобы преобразовать страницы в изображения.
Как я могу получить PDF-файл в виде файла из Uri, возвращенного MediaStore?


person Joel Broström    schedule 31.03.2020    source источник
comment
Вам действительно нужен экземпляр File или просто доступ к данным через InputStream или тому подобное?   -  person CodeRed    schedule 11.04.2020
comment
я вижу несколько вариантов использования, где вам действительно нужен файл или путь к файлу. Боюсь, я не нашел решения. очень странный   -  person M. Usman Khan    schedule 22.07.2020
comment
Да. Я не могу вспомнить точный вариант использования, но это как-то связано с загрузкой PDF-файлов на сервер после их редактирования. Нам также нужно было извлечь изображения из PDF, и для этого нам нужен был реальный файл.   -  person Joel Broström    schedule 23.07.2020
comment
@M.UsmanKhan M.UsmanKhan Я вижу несколько случаев использования, когда вам действительно нужен файл или путь к файлу - нет требования, чтобы ACTION_GET_CONTENT возвращал Uri, представляющий файл в файловой системе. См. stackoverflow.com/a/59123287/115145 и stackoverflow.com/a/59911702/115145 и stackoverflow.com/a/56308643/115145 больше.   -  person CommonsWare    schedule 23.07.2020
comment
Приложение @CommonsWare уже имеет права доступа к файлу от пользователя. Кроме того, как заставить пользователя выбрать видеофайл (большой) и получить путь к нему, не копируя его? мы получаем только контент URI, верно? я не мог найти решение этой простой проблемы   -  person M. Usman Khan    schedule 24.07.2020
comment
@M.UsmanKhan: Приложение уже имеет права доступа к файлам от пользователя - это не имеет значения. Кроме того, как заставить пользователя выбрать видеофайл (большой) и получить путь к нему, не копируя его? -- самое близкое, что вы можете сделать, это представить список видео самостоятельно на основе запроса видео из MediaStore. Затем попробуйте использовать значение столбца DATA из MediaStore в качестве пути к файловой системе. Это будет ненадежно, так как MediaStore имеет доступ к файлам, которых нет у вашего приложения (например, на съемном носителе), и неясно, можно ли будет использовать столбец DATA в будущем.   -  person CommonsWare    schedule 24.07.2020
comment
@CommonsWare спасибо. Но что, если мои пользователи могут дать мне любое разрешение? Почему так сложно достичь этой простой цели. Думаю, мне придется использовать стороннюю библиотеку.   -  person M. Usman Khan    schedule 24.07.2020
comment
@M.UsmanKhan: Но что, если мои пользователи могут дать мне любое разрешение? -- это не имеет значения. Думаю, мне придется использовать стороннюю библиотеку - это не поможет. Почему так трудно достичь этой простой цели, потому что файлы в файловой системе являются лишь одним из многих источников данных, а контент в Интернете является важной альтернативой. Итак, Google решил использовать абстракции, чтобы данные могли поступать из любого источника, локального или удаленного.   -  person CommonsWare    schedule 24.07.2020


Ответы (2)


Вот как я получаю pdf как файл:

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
                            type = "application/pdf"
                            addCategory(Intent.CATEGORY_OPENABLE)
                            flags = flags or Intent.FLAG_GRANT_READ_URI_PERMISSION
                        }
                        startActivityForResult(intent, 111)

Внутри вашего OnActivityResult(requestCode:Int,resultCode:Int,data:Intent?)

if (resultCode == Activity.RESULT_OK) {
            when (requestCode) {
//                101 -> {
//                    data?.data?.also { uri ->
//                        Log.i(TAG, "Uri: $uri")
//                        baseAdapter?.add(ImageArray(null, null, uri.toString()))
//                    }
//                }
                111 -> {
                    data?.data?.also { documentUri ->
                        baseActivity.contentResolver?.takePersistableUriPermission(
                                documentUri,
                                Intent.FLAG_GRANT_READ_URI_PERMISSION
                        )
                        val file = DocumentUtils.getFile(baseActivity,documentUri)//use pdf as file 
                    }
                }
            }

        }

Класс Singleton для преобразования Uri в файл:

object DocumentUtils {
    fun getFile(mContext: BaseActivity?, documentUri: Uri): File {
        val inputStream = mContext?.contentResolver?.openInputStream(documentUri)
        var file =  File("")
        inputStream.use { input ->
            file =
                File(mContext?.cacheDir, System.currentTimeMillis().toString()+".pdf")
            FileOutputStream(file).use { output ->
                val buffer =
                    ByteArray(4 * 1024) // or other buffer size
                var read: Int = -1
                while (input?.read(buffer).also {
                        if (it != null) {
                            read = it
                        }
                    } != -1) {
                    output.write(buffer, 0, read)
                }
                output.flush()
            }
        }
        return file
    }
} 

P.S: Не забудьте спросить разрешение во время выполнения

Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE
person chand mohd    schedule 24.07.2020
comment
Кто-нибудь может подтвердить, что это действительно работает? В данный момент я завален, но не хотел бы, чтобы награда пропадала зря. @M.UsmanKhan может быть? - person Joel Broström; 29.07.2020
comment
Вот как я получаю pdf файл и отправляю на сервер - person chand mohd; 29.07.2020

С последними обновлениями API Android подтолкнул использование преобразователя контента для обработки файлов.

Используя полученный путь, его необходимо разрешить с помощью преобразователя содержимого..

Образцы доступны в Google Github.

Чтобы дать обзор, URI, полученный после выбора файла, необходимо передать, как показано ниже во фрагменте.

Создайте фрагмент для отображения PDF. И будет связан ViewModel.

val parcelFileDescriptor = activity?.contentResolver?.openFileDescriptor(fileURI, "r")

В ViewModel с помощью Coroutine мы будем использовать процесс, как показано ниже.

val scope = CoroutineScope(executor.asCoroutineDispatcher() + job)

scope.launch {
            openPdfRenderer()
            showPage(0)
        }

fun openPdfRenderer() {
        
        pdfRenderer = PdfRenderer(fileURI!!) //android.graphics.pdf.PdfRenderer
    }

fun showPage(index: Int) {

        currentPage?.let { page ->
            currentPage = null
            page.close()
        }
        pdfRenderer?.let { renderer ->

            val page = renderer.openPage(index).also {
                currentPage = it
            }
            val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
            page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_PRINT)
            _pageBitmap.postValue(bitmap)
            val count = renderer.pageCount
            _pageInfo.postValue(index to count)
            _previousEnabled.postValue(index > 0)
            _nextEnabled.postValue(index + 1 < count)
        }
    }

Полный исходный код примера можно найти по Ссылка на образец Google Github

Согласен, может показаться, что это перебор! Но таковы и изменения в Android Geography по нескольким причинам.

Удачного кодирования! Ваше здоровье !

person Sreehari    schedule 28.07.2020
comment
Это по-прежнему не дает мне файл, а только URI, который затем используется для рендеринга PDF? Нам нужен способ получить фактический файл или создать его копию, с которой мы могли бы работать. - person Joel Broström; 29.07.2020
comment
Для этого вопроса мне нужен файл, чтобы преобразовать страницы в изображения. Это уже сделано в приведенном образце. И отображается во фрагменте PDF Viewer как само изображение. Но он не позволяет создавать или копировать файл, которым вы можете манипулировать. - person Sreehari; 29.07.2020
comment
Если вы просто хотите показать предварительный просмотр содержимого PDF во фрагменте, этого будет достаточно. - person Sreehari; 29.07.2020