- Uri с com.android.externalstorage.documents, расположенными на не первичной томе
- Загрузить файл из внешнего хранилища с помощью селектора
- 2 ответа
- Как некоторые приложения получают доступ к содержимому подпапок «… / Android /…» на Android 11 без рута?
- Задний план
- Проблема
- Что я нашел и попробовал
- Вопросы
- 3 ответа
- Не удалось найти часть пути «/content:/com.android.externalstorage.documents/document/6759-130B%3ANew%20Text%20Document.txt»
- 1 ответ
- URI изображения не отображает изображения в ImageView на некоторых устройствах Android
- 12 ответов
Uri с com.android.externalstorage.documents, расположенными на не первичной томе
У меня проблема. У меня есть Uri который читает что-то вроде:
Я могу найти StorageVolume через StorageManager.getStorageVolumes()
Если объем является основным: StorageVolume.isPrimary() — я понимаю, что могу разобрать путь как:
Но как найти путь, если объем не является основным? В конце концов, мне нужно иметь простой объект File ссылается его абсолютный путь.
PS Конечно, у меня есть все необходимое разрешение: либо статические объявлены через манифест или во время выполнения обоих.
Я понимаю, что могу разобрать путь как
Не надежно. Вы принимаете определенную внутреннюю реализацию кода, который может отличаться от производителя устройства и версии ОС.
В конце концов, мне нужно иметь простой объект File, на который ссылается его абсолютный путь.
Шаг # 1: используйте ContentResolver и openInputStream() чтобы получить InputStream на содержимое, идентифицированное Uri
Шаг № 2: Откройте FileOutputStream на некоторый файл, который вы контролируете (например, в getCacheDir() )
Шаг 3: Скопируйте содержимое из InputStream в FileOutputStream
Шаг 4: используйте полученный файл, удалив его, когда он больше не нужен
Конечно, у меня есть все необходимое разрешение
Конечно, нет, поскольку у вас нет разрешений на доступ к файлам на съемном носителе, вне очень специфических мест (например, второй и последующие пути, возвращаемые getExternalFilesDirs() ).
Источник
Загрузить файл из внешнего хранилища с помощью селектора
Итак, я пытаюсь загрузить простой текстовый файл, например:
И, конечно же, поймать такой результат:
Он отлично работает на genymotion и на моем устройстве, если я использую файловый проводник, который я установил (проводник, см. Изображение выше), теперь, если использовать селектор прямо следующим образом:
Он говорит, что не может найти указанный файл. (FileNotFoundException)
Теперь я понял, что URI, которые я получаю от этих двух средств выбора файлов, разные.
content: //com.android.externalstorage.documents/document/primary%3ADownload%2Ffile.txt 4
2 ответа
Я попытался использовать преобразователь содержимого, чтобы получить путь к файлу из URI следующим образом
Путь к файлу отсутствует. Uri не обязательно указывает на file, к которому вы можете получить доступ в своей локальной файловой системе, так же как URL-адрес этой веб-страницы не обязательно указывает на файл, к которому вы можете получить доступ в своей локальной файловой системе. Uri может:
- представляют файл, хранящийся во внутренней памяти другого приложения
- представляют значение столбца BLOB в базе данных
- представляют собой файл, хранящийся в «облаке», который будет загружен по запросу
- и т.п.
Используйте ContentResolver и такие методы, как openInputStream() , чтобы читать содержимое, представленное Uri , точно так же, как вы использовали бы HttpUrlConnection для чтения содержимого, представленного URL-адресом для этого Страница в Интернете. openInputStream() принимает Uri в качестве параметра и возвращает InputStream . Вы бы использовали этот InputStream так же, как и любой другой InputStream (например, FileInputStream , InputStream из HttpUrlConnection ). Точный механизм этого будет во многом зависеть от базовых данных (например, читать в строку, перейдите к BitmapFactory , чтобы декодировать Bitmap ).
Ответ @ CommonsWare должен быть правильным способом решения этой проблемы, но я не уверен, как реализовать его решение. В итоге я сделал то, что рекомендует @Paul Burke по этой ссылке:
ИЗМЕНИТЬ
В моем конкретном случае так закончился мой код. Я оставляю это здесь на будущее. (Я использую объяснение @ CommonsWare) см. Его ответ выше.
Источник
Как некоторые приложения получают доступ к содержимому подпапок «… / Android /…» на Android 11 без рута?
Задний план
Существуют различные ограничения хранилища для Android 10 и 11 , который также включает новое разрешение ( MANAGE_EXTERNAL_STORAGE) для доступа все файлы (но он не разрешает доступ действительно ко всем файлам), в то время как предыдущее разрешение на хранение было уменьшено, чтобы предоставить доступ только к медиафайлам:
- Приложения могут свободно обращаться к подпапке «media».
- Приложения никогда не могут получить доступ к подпапке «данные» и особенно к содержимому.
- Для папки «obb», если приложению разрешено устанавливать приложения, оно может добраться до нее (скопировать туда файлы). Иначе не может.
- Используя USB или root, вы все равно можете связаться с ними, а как конечный пользователь вы можете связаться с ними через встроенный файловый менеджер «Files».
Проблема
Я заметил приложение, которое каким-то образом преодолевает это ограничение (здесь) с названием» X-plore»: после входа в папку «Android / data» вас попросят предоставить к ней доступ. (как-то напрямую используя SAF), и когда вы его предоставите, вы получите доступ ко всему во всех папках папки «Android».
Это означает, что, возможно, еще есть способ достичь этого, но проблема в том, что я по какой-то причине не смог сделать образец, который делает то же самое.
Что я нашел и попробовал
Кажется, это приложение нацелено на API 29 (Android 10), и что оно еще не использует новое разрешение, и что у него есть флаг requestLegacyExternalStorage. Я не знаю, сработает ли тот же трюк, который они используют, при таргетинге на API 30, но могу сказать, что в моем случае, работающем на Pixel 4 с Android 11, он работает нормально.
Итак, я попытался сделать то же самое:
Я сделал образец POC, предназначенный для Android API 29, с предоставленными разрешениями на хранилище (всех видов), включая устаревший флаг.
Я попытался запросить доступ напрямую к папке «Android» (на основе здесь), который, к сожалению, не сработал по какой-то причине (продолжал переходить в папку DCIM, не знаю почему):
Пробовал разные комбинации флагов.
При запуске приложения, когда я сам добираюсь до папки «Android» вручную, так как это не сработало, я предоставил доступ к этой папке, как и к другому приложению.
Получив результат, я пытаюсь получить файлы и папки по пути, но получить их не удается:
Таким образом, с помощью DocumentFile.fromTreeUri я все еще мог получить просто папку «media», которая бесполезна, и, используя класс File, я мог видеть только, что есть также папки «data» и «obb», но все еще не мог добраться до их содержимого .
Так что это вообще не сработало.
Позже я обнаружил другое приложение, использующее этот трюк, под названием «MiXplorer». В этом приложении ему не удалось напрямую запросить папку «Android» (возможно, он даже не пытался), но он предоставляет вам полный доступ к ней и ее подпапкам, как только вы это разрешите. И он нацелен на API 30, поэтому это означает, что он не работает только потому, что вы нацеливаетесь на API 29.
Я заметил (кто-то написал мне), что с некоторыми изменениями в коде я могу запросить доступ к каждой из подпапок отдельно (имеется в виду запрос на «данные» и новый запрос на «obb»), но это не то, что я здесь вижу, а приложения.
То есть, чтобы попасть в папку «Android», я использую этот Uri в качестве параметра для Intent.EXTRA_INITIAL_URI:
Однако, как только вы получите к нему доступ, вы не сможете получить из него список файлов ни через File, ни через SAF.
Но, как я уже писал, странно то, что если вы попробуете что-то подобное, вместо того чтобы перейти к «Android / data», вы сможете получить его содержимое:
Вопросы
- Как я могу запросить намерение непосредственно в папке «Android», которое фактически позволит мне получить к нему доступ и позволит мне получить подпапки и их содержимое?
- Есть ли для этого другая альтернатива? Может быть, используя adb и / или root, я мог бы предоставить SAF доступ к этой конкретной папке?
3 ответа
Вот как это работает в X-plore:
Когда на Build.VERSION.SDK_INT>=30 , [Внутреннее хранилище] / Android / data недоступно, java File.canRead() или File.canWrite() возвращает false, поэтому нам нужно переключиться на альтернативную файловую систему для файлов внутри этой папки (и, возможно, также obb).
Вы уже знаете, как работает структура доступа к хранилищу, поэтому я просто подробно расскажу, что именно нужно сделать.
Вы звоните ContentResolver.getPersistedUriPermissions() , чтобы узнать, есть ли у вас уже сохраненное разрешение для этой папки. Изначально у вас его нет, поэтому вы спрашиваете разрешения у пользователя:
Чтобы запросить доступ, используйте startActivityForResult с Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).putExtra(DocumentsContract.EXTRA_INITIAL_URI, DocumentsContract.buildDocumentUri(«com.android.externalstorage.documents», «primary:Android»)) Здесь вы устанавливаете с помощью EXTRA_INITIAL_URI , что средство выбора должно запускаться непосредственно в папке Android в основном хранилище, потому что нам нужен доступ к папке Android. Когда ваше приложение будет нацелено на API30, средство выбора не позволит выбрать корень хранилища, а также, получив разрешение на папку Android, вы можете работать как с папками data , так и obb внутри, с одним запросом разрешения. .
Когда пользователь подтверждает двумя щелчками мыши, в onActivityResult вы получите Uri в данных, который должен быть content://com.android.externalstorage.documents/tree/primary%3AAndroid . Сделайте необходимые проверки, чтобы убедиться, что пользователь подтвердил правильность папки. Затем позвоните contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION) , чтобы сохранить разрешение, и все готово.
Итак, мы вернулись к ContentResolver.getPersistedUriPermissions() , который содержит список предоставленных разрешений (их может быть больше), тот, который вы предоставили выше, выглядит так: UriPermission
Теперь вы хотите работать с ContentResolver и DocumentsContract , используя этот «древовидный» uri и ваш относительный путь к файлам внутри папки Android. Вот пример для вывода списка папок data : data/ — это путь относительно предоставленного uri «дерева». Создайте окончательный uri, используя либо DocumentsContract.buildChildDocumentsUriUsingTree() (для вывода списка файлов), либо DocumentsContract.buildDocumentUriUsingTree() (для работы с отдельными файлами), например: DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, DocumentsContract.getTreeDocumentId(treeUri), DocumentsContract.getTreeDocumentId(treeUri)+»/data/») , вы получите uri = content://com.android.externalstorage.documents/tree/primary%3AAndroid/document/primary%3AAndroid%2Fdata%2F/children , подходящий для перечисление файлов в папке данных. Теперь вызовите ContentResolver.query(uri, . ) и обработайте данные в Cursor , чтобы получить список папок.
Подобным образом вы работаете с другими функциями SAF для чтения / записи / переименования / перемещения / удаления / создания, которые вы, вероятно, уже знаете, используя ContentResolver или методы DocumentsContract .
- ему не нужно android.permission.MANAGE_EXTERNAL_STORAGE
- он работает на целевом API 29 или 30
- он работает только с основным хранилищем, а не с внешними SD-картами
- для всех файлов внутри папки данных вам необходимо использовать SAF (файл java не будет работать), просто используйте иерархию файлов относительно папки Android
- в будущем Google может залатать эту дыру в своих намерениях «безопасности», и это может не сработать после некоторого обновления безопасности.
РЕДАКТИРОВАТЬ: образец кода, основанный на образце Github Cheticamp. В примере показано содержимое (и количество файлов) каждой из подпапок папки «Android»:
Имея ту же проблему, я использовал приведенный ниже код для создания новой папки / подпапки во внешнем хранилище. он также работает в Android 11 с устройством пикселей 2xl и пикселей 3
Проверьте это и дайте мне знать, если возникнут проблемы
Для этого не требуется никакого специального разрешения
андроид : requestLegacyExternalStorage = » истина»
В вашем файле манифеста и проверьте
Ну, я попробовал этот код, и он работает на Android API 29, Samsung Galaxy 20FE:
Я вызываю это с помощью кнопки onClick:
В пользовательском интерфейсе я нажимаю «показать внутреннюю память», перехожу в каталог Android и нажимаю «разрешить». После этого при отладке, если я попытаюсь перечислить все файлы под android, я получаю список всех каталогов в Data. Если это то, что вы ищете.
Источник
Не удалось найти часть пути «/content:/com.android.externalstorage.documents/document/6759-130B%3ANew%20Text%20Document.txt»
Примечание: У меня есть права на чтение и запись, а также я спрашиваю пользователя во время выполнения.
При использовании этого пути проблема исчезла:
Это очень известная старая ошибка в средстве выбора как официально упоминается.
На мой взгляд, мне нужно преобразовать content: // в путь к файлу .
1 ответ
Я вспомнил, что вам нужно получить абсолютный путь от вашего uri. Обычно путь content: // возвращается из папки загрузки или любого пути к диску, вам нужно получить для него фактический путь. Вы можете попробовать это.
Это специфичный для устройства код для Android, введите его с помощью службы зависимостей.
Я столкнулся с вышеуказанной проблемой в родном Android и решил, что, как показано ниже, в Java , ниже приведен преобразованный код для того же в c#
Позже новый путь к файлу используется везде, где это необходимо.
Именно это я и делаю в своем проекте, какой бы файл ни выбрал пользователь, он копируется в Pictures Dir во внутренней памяти и возвращает путь.
Мы имеем дело с ImageStream , чтобы сделать копию исходного документа. Идея создания дубликата заключается в том, что мы используем копию для загрузки, поскольку пользователь может удалить выбранный исходный документ. Поэтому, отправив документ на сервер, мы также удаляем скопированный файл. Так как мы имеем дело с потоком, у нас не возникает никаких проблем с Content:// .
Источник
URI изображения не отображает изображения в ImageView на некоторых устройствах Android
У меня есть ImageView. Когда вы щелкаете ImageView, открывается галерея, вы выбираете изображение и показываете его в ImageView. У меня есть условие, что когда я закрываю приложение, а затем открываю его, изображение сохраняется там. Поэтому для этого я сохраняю изображение Uri на sharedprefrence. И при открытии приложения я получаю тот же Uri и пытаюсь отобразить изображение в imageView.
Однако в некоторых телефонах изображение выглядит идеально, как Mi (Lollipop), Samsung (KitKat), но не отображается в телефонах, таких как Motorola (Marshmallow), One Plus One (Marshmallow). Есть идеи, почему это происходит? Вот мой код
Для получения изображения я использую
А в OnActivityResult () код
И при получении из общих предпочтений я конвертирую String в uri с помощью Uri.parse(profile_image)
Однако, если я заметил, uri возвращается для другого телефона Android следующим образом
Следовательно, когда содержимое uri -content: // media / external / images / media / является отображаемым изображением в ImageView Perfect, а в других случаях это не так.
12 ответов
Я разработал, tested и работаю над
- Lenovo K3 Note (Зефир)
- Motorola (леденец)
- Samsung (KitKat)
В вашем MainActivity.java добавьте
Теперь обработайте onActivityResult .
В вашем файле Manifest добавьте permission
Примечание.
Assuming вы добавили код для take permission для Marshmallow
Результат Uri
lenovo K3 Примечание: content://com.android.externalstorage.documents/document/primary%3ADCIM%2FCamera%2FIMG_20160606_212815.jpg
samsung: content://com.android.providers.media.documents/document/image%3A2086
моторола: content://com.android.providers.media.documents/document/image%3A15828
Простое исправление — просто добавить «file: //» в начало пути к файлу. Например: изменение <
Просто не уверен на 100%, получу ли я вопрос. Если определенные изображения не могут отображаться в imageView (даже до закрытия и перезапуска приложения), проблема в том, что Picasso может обрабатывать содержимое от определенных поставщиков. Я не понимаю, что это так, но если это так, то вы можете подумать о регистрации ошибки / RFE для этой библиотеки. С другой стороны, если Пикассо правильно показывает изображение для данного Uri, но не после преобразования Uri_org -> String -> Uri_regen, это означает, что Uri_org не то же самое, что Uri_regen. Вы можете (временно) добавить пару строк в свой код, чтобы преобразовать String обратно в Uri, а сразу после этого поставить точку останова и сравнить два Uri. Это должно дать вам представление о том, как дела идут не так.
Если вы не достигли точки останова, переместите точку останова обратно в оператор «if» и визуально проверьте два URI (которые почти наверняка будут одинаковыми, иначе вы бы достигли точки останова, но это поможет проверить отладку сам код). Если Uri одинаковы, то почему-то предпочтение String по какой-то причине не сохраняется правильно. В этом случае запишите исходный Uri и добавьте точку останова в строке, которая преобразует String обратно в Uri:
Если предыдущий эксперимент сработал, он должен снова работать здесь, если только profile_image не отличается. В любом случае вы должны указать, в чем проблема.
Я также сталкивался с чем-то подобным ранее и нашел полезный вспомогательный класс, который дает вам точное местоположение файла. Вот ссылка.
Вы можете использовать такие методы, как FileUtils.getPath (context, uri) или FileUtils.getFile (context, uri), чтобы получить файл. Тогда просто используйте это так:
Надеюсь это поможет.
Может быть проблема с версией Пикассо. Они исправили кое-что с загрузкой больших изображений в версии 2.5.0 (2.4.0 . ), Что пока недоступно в репозиториях.
Вам нужно будет вручную включить ночной снимок Пикассо, и это, скорее всего, решит вашу проблему. Это сработало для меня.
Вы можете легко преобразовать URI в Bitmap:
Затем установите Imageview: imgview.setImageBitmap (image);
Надеюсь, что это поможет вам.
Мне удавалось это делать раньше. Вместо того, чтобы хранить его в SharePreferences, я сохраняю его в Bundle, когда onSaveInstanceState (Bundle) вызывается по: bundle.putParcelable (SOME_KEY, uri). Вы можете сделать это, потому что Uri реализует Parcelable. После этого проверьте этот uri в Bundle, переданном в onCreate (). Код:
P / S: Думаю, проблема с сохранением запроса в Shared Preference заключается в кодировании / декодировании Uri.
Возможно, вы справитесь с этим, сначала преобразовав URI в реальный путь.
И ваш onActivityResult должен выглядеть так
Вам необходимо запросить разрешение на чтение внешнего хранилища пользователю во время выполнения для Android 6+.
Надеюсь это поможет.
Передайте свой URI в этом методе, он вернет String, а затем преобразует его в Bitmap.
«В предыдущих версиях Android, если вы хотите, чтобы ваше приложение получало файл определенного типа из другого приложения, оно должно вызывать намерение с действием ACTION_GET_CONTENT . Это действие по-прежнему является подходящим способом запросить файл, который вы хотите импортировать в свое приложение. Однако в Android 4.4 представлено действие ACTION_OPEN_DOCUMENT , которое позволяет пользователю выбрать файл определенного типа и предоставить вашему приложению долгосрочный доступ для чтения к этому файлу . (возможно, с доступом на запись) без импорта файла в ваше приложение »(выделено мной)
Чтобы ваше приложение могло получать ранее выбранные изображения, просто измените действие с ACTION_GET_CONTENT на ACTION_OPEN_DOCUMENT (решение подтверждено, работающее на Nexus 7 2013).
Я думаю, что проблема с uri файла изображения, я использую класс для получения точного URI, и он протестирован и работает на всех версиях.
Используйте getPath, чтобы найти точный путь к файлу. Это самый простой способ решить проблему, как вы определили. Пожалуйста, дайте мне знать, если вам понадобится дополнительная помощь 🙂
Источник