- Преобразование файла: Uri в файл в Android
- 15 ответов
- file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat). And here is the solution.
- Real example with a crashing problem
- Why Nougat does not allow passing file:// with Intent anymore?
- Solution
- How about the existed app launched previously?
- Преобразование файла: Uri в файл в Android
- 15 ответов
- Как использовать содержимое из Uri
- Что можно делать?
- Чего не стоит делать?
- Просто забрать путь
- Представить, что MediaStore знает, чего вы хотите
- Попробовать извлечь путь
- Как получить имя файла?
- Хорошо, тогда как мне получить расширение файла?
- Что делать, если мне нужно передать файл в библиотеку?
- Как долго я могу использовать Uri?
- Но что делать, если мне понадобится контент на более длительное время?
- Зачем столько боли?
Преобразование файла: Uri в файл в Android
какой самый простой способ конвертировать из file: android.net.Uri до File В Android?
пробовал следующее, Но это не работает:
15 ответов
то, что вы хотите.
Примечание: uri.toString() возвращает строку в формате: «file:///mnt/sdcard/myPicture.jpg» , тогда как uri.getPath() возвращает строку в формате: «/mnt/sdcard/myPicture.jpg» .
после долгого поиска это то, что сработало для меня:
EDIT: Извините, я должен был протестировать лучше раньше. Это должно сработать:
ничего из этого не работает для меня. Я нашел это рабочим решением. но мой случай специфичен для изображений.
непосредственно и скопируйте файл. См. также:
С Котлином еще проще:
Я сделал это следующим образом:
поэтому в основном сначала я пытаюсь использовать файл, т. е. снимок, сделанный камерой и сохраненный на SD-карте. Это не работает для изображения возвращаемых: Intent photoPickerIntent = новое намерение(намерение.ACTION_PICK); В этом случае необходимо преобразовать Uri в реальный путь по
@CommonsWare объяснил все вещи довольно хорошо. И мы действительно должны использовать предложенное им решение.
кстати, только на информацию мы могли положиться, когда quering ContentResolver — Это имя и размер файла, как указано здесь: получение информации о файле / разработчики Android
как вы могли видеть, есть интерфейс OpenableColumns который содержит только два поля: DISPLAY_NAME и SIZE.
в моем случае мне нужно было получить информацию EXIF о JPEG изображение и повернуть его, если это необходимо перед отправкой на сервер. Для этого я скопировал содержимое файла во временный файл с помощью ContentResolver и openInputStream()
Лучшим Решением
создайте один простой класс java FileUtil и используйте для создания, копирования и переименования файла
Я использую uri.toString() и uri.getPath() но не работает для меня. Я наконец нашел решение.
пользовательский класс FileUtil в вашем коде
вы можете получить файл из файла Uri, используя следующий метод. Он DocumentFile для параметра, но вы можете легко изменить его. Самый простой способ проверить этот метод для создания файла uri с помощью DocumentFile.fromFile(Environment.getExternalStorageDirectory()); который имеет Uri file:///storage/emulated/0
вместо создания файлов из Uri используйте DocumentFile.fromTreeUri(Context, Uri) или DocumentFile.fromSingleri(Context, Uri) для создания или удаления файлов и для потоков используйте getContentResolver().openOutputStream(documentFile.getUri()) и getContentResolver().openInputStream(documentFile.getUri()) для записи файлов и чтения файлов.
публичная строка getRealPathFromURI(Uri uri) <
затем, используя для получения файла из URI:
— НАДЕЖДА МОЖЕТ ПОМОЧЬ ВАМ — — — —
Android + Котлин
добавить зависимость для расширений Kotlin Android:
получить файл из uri:
Android + Java
просто переместить наверх 😉
следующим кодом я могу получить общий pdf-файл adobe application в виде потока и сохранить его в путь приложения android
для людей, которые ищут решение для изображений в частности вот это.
Источник
file:// scheme is now not allowed to be attached with Intent on targetSdkVersion 24 (Android Nougat). And here is the solution.
Android Nougat is almost be publicly released. And as an Android developer, we need to prepare ourself to adjust targetSdkVersion to the latest one, 24 , to let everything works perfectly on the newest release of Android.
And as always, everytime we adjust targetSdkVersion, we need to check and make sure that every single part of our code works perfectly fine. If you just simply change the number, I could say that your application is taking a high risk of crashing or malfunction. In this case, when you change your app’s targetSdkVersion to 24, we need to check that every single function works flawlessly on Android Nougat (24).
And this is one of the checklist you need to mark done before releasing your new version. There is one big security change on Android N like quoted below:
Passing file:// URIs outside the package domain may leave the receiver with an unaccessible path. Therefore, attempts to pass a file:// URI trigger a FileUriExposedException . The recommended way to share the content of a private file is using the FileProvider .
Summarily, file:// is not allowed to attach with Intent anymore or it will throw FileUriExposedException which may cause your app crash immediately called.
This blog will talk about this issue and also about the solution how to make it work on Android N.
Real example with a crashing problem
You may be curious which situation that can really cause the problem. So to make it be easy to you all, let me show you a real usage example that causes crashing. The easiest example is the way we take a photo through Intent with ACTION_IMAGE_CAPTURE type. Previously we just pass the target file path with file:// format as an Intent extra which works fine on Android Pre-N but will just simply crash on Android N and above.
Here is the code. Please note that you can find and download it from GitHub.
When code above is run, there will be a Button shown on screen. Once Button is clicked, camera app will be launched to let you take a photo. After everything is done, the taken photo will be shown on the ImageView as a result.
What the code does is quite straightforward. Generated file path pointed to DCIM folder under External Storage will be sent to camera app in file:// format and will be used as file path of a photo taken.
Code above works fine even on Android Nougat since targetSdkVersion is still be 23. Now let’s change it to 24.
Here is the result. It still works on Android Pre-N but it appears to cause crashing on Android Nougat like this:
And here is the stacktrace.
The reason is quite obvious. file:// is not allowed as an attached URI in Intent or FileUriExposedException would be thrown.
And this is a big issue that you have to make sure that all code related to this case has already been fixed before releasing a new version with targetSdkVersion 24 or your app may crash on some of your user’s device.
Why Nougat does not allow passing file:// with Intent anymore?
You may be curious why Android team decide to change this behavior. Actually there is a good reason behind.
If file path is sent to the target application (Camera app in this case), file will be fully accessed through the Camera app’s process not the sender one.
But let’s consider thoroughly, actually Camera is launched by our application to take a photo and save as a file on our app’s behalf. So the access right to that file should be our app’s not Camera’s. Every operation did with the file should be done through our application not by Camera app itself.
And that’s why file:// is now prohibited on targetSdkVersion 24 to force every developer to do this task in the proper way.
Solution
So if file:// is not allowed anymore, which approach should we go for? The answer is we should send the URI through content:// scheme instead which is the URI scheme for Content Provider. In this case, we would like to share an access to a file through our app so FileProvider is needed to be implemented. Flow is now changed like below:
And now, with FileProvider, file operation would be done through our app process like it supposes to be !
It is quite easy to implement FileProvider on your application. First you need to add a FileProvider
tag in AndroidManifest.xml under tag like below:
AndroidManifest.xml
And then create a provider_paths.xml file in xml folder under res folder. Folder may be needed to create if it doesn’t exist.
The content of the file is shown below. It describes that we would like to share access to the External Storage at root folder (path=».») with the name external_files.
res/xml/provider_paths.xml
Done! FileProvider is now declared and be ready to use.
The final step is to change the line of code below in MainActivity.java
And . done ! Your application should now work perfectly fine on any Android version including Android Nougat. Yah !
How about the existed app launched previously?
As you can see from the experiment result above. This behavior will happen only when you change your app’s targetSdkVersion to 24 or above. So if your previously launched application was set the targetSdkVersion to 23 or lower, it supposes not to be a problem even on Android Nougat. file:// should still works perfectly fine.
Anyway, to match the Android Best Practice, when there is a new API Level, we better always change targetSdkVersion to the latest one for the best user experience. =)
Источник
Преобразование файла: Uri в файл в Android
какой самый простой способ конвертировать из file: android.net.Uri до File В Android?
пробовал следующее, Но это не работает:
15 ответов
то, что вы хотите.
Примечание: uri.toString() возвращает строку в формате: «file:///mnt/sdcard/myPicture.jpg» , тогда как uri.getPath() возвращает строку в формате: «/mnt/sdcard/myPicture.jpg» .
после долгого поиска это то, что сработало для меня:
EDIT: Извините, я должен был протестировать лучше раньше. Это должно сработать:
ничего из этого не работает для меня. Я нашел это рабочим решением. но мой случай специфичен для изображений.
непосредственно и скопируйте файл. См. также:
С Котлином еще проще:
Я сделал это следующим образом:
поэтому в основном сначала я пытаюсь использовать файл, т. е. снимок, сделанный камерой и сохраненный на SD-карте. Это не работает для изображения возвращаемых: Intent photoPickerIntent = новое намерение(намерение.ACTION_PICK); В этом случае необходимо преобразовать Uri в реальный путь по
@CommonsWare объяснил все вещи довольно хорошо. И мы действительно должны использовать предложенное им решение.
кстати, только на информацию мы могли положиться, когда quering ContentResolver — Это имя и размер файла, как указано здесь: получение информации о файле / разработчики Android
как вы могли видеть, есть интерфейс OpenableColumns который содержит только два поля: DISPLAY_NAME и SIZE.
в моем случае мне нужно было получить информацию EXIF о JPEG изображение и повернуть его, если это необходимо перед отправкой на сервер. Для этого я скопировал содержимое файла во временный файл с помощью ContentResolver и openInputStream()
Лучшим Решением
создайте один простой класс java FileUtil и используйте для создания, копирования и переименования файла
Я использую uri.toString() и uri.getPath() но не работает для меня. Я наконец нашел решение.
пользовательский класс FileUtil в вашем коде
вы можете получить файл из файла Uri, используя следующий метод. Он DocumentFile для параметра, но вы можете легко изменить его. Самый простой способ проверить этот метод для создания файла uri с помощью DocumentFile.fromFile(Environment.getExternalStorageDirectory()); который имеет Uri file:///storage/emulated/0
вместо создания файлов из Uri используйте DocumentFile.fromTreeUri(Context, Uri) или DocumentFile.fromSingleri(Context, Uri) для создания или удаления файлов и для потоков используйте getContentResolver().openOutputStream(documentFile.getUri()) и getContentResolver().openInputStream(documentFile.getUri()) для записи файлов и чтения файлов.
публичная строка getRealPathFromURI(Uri uri) <
затем, используя для получения файла из URI:
— НАДЕЖДА МОЖЕТ ПОМОЧЬ ВАМ — — — —
Android + Котлин
добавить зависимость для расширений Kotlin Android:
получить файл из uri:
Android + Java
просто переместить наверх 😉
следующим кодом я могу получить общий pdf-файл adobe application в виде потока и сохранить его в путь приложения android
для людей, которые ищут решение для изображений в частности вот это.
Источник
Как использовать содержимое из Uri
Поскольку становится всё более очевидным, что схема file прекращает своё существование, давайте рассмотрим, как всё это будет работать сейчас.
Посмотрим на такую ситуацию со стороны потребителя: вам передают Uri с каким-нибудь контентом в случае, когда раньше вы ожидали путь к файлу. Как вы будете получать этот контент?
Что можно делать?
Во-первых, убедитесь, что в любом месте, где у вас есть для схемы file, у вас также есть для content, с областью видимости для соответствующего MIME типа. Вы можете получить контент Uri другими способами (например, в onActivityResult()), но если вы поддерживаете файл-подобный контент для таких действий, как ACTION_VIEW, убедитесь, что вы поддерживаете как file, так и content Uri.
Следующим шагом поймите, какую роль играет Uri. Некоторые значения Uri указывают на записи базы данных, например, если вы предлагаете пользователю выбрать контакт. Такие случаи в данный момент нам неинтересны. Другие значения указывают на вещи, которые могут какими-то файлами — вот для этого мы и нужна эта статья.
Затем, если Uri имеет схему content, вы можете сделать следующее с экземпляром ContentResolver:
- Вызовите openInputStream() для чтения контента.
- Вызовите getType(), чтобы получить тип MIME данных.
- Вызовите query(), запрашивающий OpenableColumns, в котором вы сможете получить размер контента и некоторую разновидность отображаемого имени, связанного с контентом.
Первые два пункта напоминают, как вы обрабатываете http или https Uri, и поэтому, если у вас есть написанный для этого код, вы можете переписать его, чтобы избежать дублирования кода.
И это, в общем-то, всё.
Чего не стоит делать?
К сожалению, хотя и не всем нравится эти решение, но многие думают, что это единственный подходящий вариант.
Просто забрать путь
Некоторые разработчики вызывают getPath() у Uri, а затем пытаются открыть его как файл (например, new File(uri.getPath())).
Не делайте так!
Этот способ никогда не был надёжным, так как он предполагает схему file и то, что у вас есть прямой доступ к местоположению файла с заданным путём.
Если у Uri есть какая-либо другая схема, например content, путь в значительной мере становится бесполезным для вас.
Попытка выбрать какие-либо отдельные части из Uri и работать с ними вряд ли окажется успешной для вас.
Представить, что MediaStore знает, чего вы хотите
Некоторые разработчики пытаются запросить MediaStore для столбца DATA в надежде, что они смогут конвертировать Uri в путь к файлу.
Не делайте так!
Не каждый content Uri поступает из MediaStore. На самом деле, в настоящее время относительно немногие Uri так делают. MediaStore ничего не знает о значениях Uri от других поставщиков. Кроме того, даже если вы получите MediaStore Uri, который может индексировать медиа на съёмном носителе, у вас не будет доступа к файловой системе.
Попробовать извлечь путь
Некоторые разработчики используют более сложный вариант для вышеупомянутых подходов, с двумя тоннами кода, чтобы посмотреть на полномочия Uri и попытаться получить путь, основанный на эвристике, разработанной для разных полномочий.
Не делайте так!
Тот факт, что это возможно, является частичной ошибкой Google, поскольку они не сделали значения Uri более скрытыми.
Однако, этот способ тоже ненадёжен:
- Приложения могут менять свои структуры Uri, и поэтому эвристика, которая работает сегодня, может не работать завтра.
- Возможно, у вас не будет доступа к файлу, даже если вы определите путь.
- Скоро случится Кембрийский взрыв приложений, публикующих собственный контент, используя своих собственных поставщиков и значения Uri, благодаря запрету на схему file. Вам необходимо поддерживать этих поставщиков, поэтому вам необходимо использовать метод openInputStream() в любом случае.
Ниже представлен список вопрос, которые могут возникнуть в данной ситуации.
Как получить имя файла?
Вы не можете этого сделать.
Вы можете посмотреть последний сегмент Uri. Это может быть имя файла, а может и не быть. Нет требования, чтобы content Uri использовал реальное имя файла в качестве последнего сегмента пути.
DISPLAY_NAME, который вы можете получить из вышеупомянутого OpenableColumns, может быть именем файла, но также может и не быть. Термин «отображаемое имя» необязательно означает имя файла.
Кроме того, в зависимости от того, откуда контент идёт, фактически никакого файла может не существовать. Предположим. что кто-то в заметку с длинной записью в приложении для заметок, и это приложение делает её доступной через Uri для доступа к другим приложениям. Пользователь не создавал файл, не загружал, не делал ничего, связанного с файлами, с этой заметкой. Даже если для неё есть подлинное имя файла, оно не будет иметь никакого значения для пользователя.
Хорошо, тогда как мне получить расширение файла?
Вы не можете этого сделать.
Ещё раз, вы можете узнать, заканчивается ли Uri тем, что выглядит как имя файла. Нет никакого гарантии, что что-то вроде foo.bar будет являться файлом с расширением .bar.
Вы можете использовать MimeTypeMap или аналогичные виды преобразователей, чтобы попытаться получить MIME тип, который вы получаете из getType(), и затем получить расширение файла. Однако эти конвертеры обрабатывают только популярные MIME типы и не могут обработать кастомные.
Что делать, если мне нужно передать файл в библиотеку?
Во-первых, проверьте библиотеку на наличие в ней варианта InputStream с методом для файла, который вы хотите использовать. Если он есть, используйте InputStream.
Затем, рассмотрите замену библиотеку на ту, которая имеет вариант InputStream.
Наконец, создайте локальную копию контента, получив InputStream из ContentResolver, подучив FileOutputStream в локальном файле (например, внутри getCacheDir() для внутреннего хранилища) и используя операции ввода-вывода для копирования из InputStream в OutputStream. flush(), getFD.sync() и close() FileOutputStream, и теперь у вас есть файл. который вы можете передать в библиотеку.
Конечно, это копия контента, поэтому она может стать устаревшей. Этот способ хорош для одноразовой операции и подходит для случаев, когда вам законным образом нужна локальная копия файла.
Как долго я могу использовать Uri?
Предполагая, что на стороне провайдера ничего не происходит, Uri должен быть действителен в течение всего процесса, но после этого он может стать недействительным.
Представьте, что Uri является глубокой ссылкой на какой-нибудь контент, требующий аутентификации. URL, который вы получаете, является рабочим, поскольку пользователь прошёл аутентификацию. URL может быть полезен даже некоторое время, пока сеанс активен. Но в итоге сеанс завершится и URL станет бесполезным.
Но что делать, если мне понадобится контент на более длительное время?
Вы можете вызвать takePersistableUriPermission() в ContentResolver. Если провайдер предлагает удержать разрешения, и вы их принимаете, то система запомнит, что у вас есть доступ к Uri. Соблюдайте осторожность, вы можете использовать метод для неограниченного доступа к контенту бескончно или до тех пор, пока пользователь не переместит, удалит или сделает недоступным к контенту по старому Uri.
Проблема в том, что takePersistableUriPermission() не говорит вам, были ли получены разрешения. Вы должен будете вызвать getPersistedUriPermissions() и посмотреть, есть ли у вас разрешения. Возможно, здесь нет разрешений, которые можно удержать.
Другой вариант — сделать локальную копию, как было отмечено ранее в FAQ. Таким образом вы сможете контролировать свою копию. Это может потребовать некоторой корректировки вашего UI и терминологии. Вместо использования глаголов типа «перейти» (предполагая, что контент находится в другом месте), используйте «импортировать», «копировать», чтобы подчеркнуть тот факт, что содержимое копируется. Вы можете предложить функции «обновить», «заменить», чтобы пользователь смог получить свежий Uri, который вы можете использовать, чтобы заменить существующую локальную копию новой.
Зачем столько боли?
Видение Google заключается в том, что значения content Uri являются эквивалентом Android http / https в веб-приложениях: универсально понятные и используемые. В конце концов, ContentResolver может сделать этот контент доступным из:
- файлов, в том числе тех, к которым другие люди не имеют доступа.
- столбцов BLOB в базе данных.
- зашифрованных материалов, которые дешифруются на лету.
- материалов, которые хранятся в сети и должны быть загружены и сохранены в кеш.
- материалов, которые генерируются на лету.
Использование значений content Uri намного более гибкое и предлагает больше различных параметров безопасности.
Источник