Путь к файлу vs URI: практический разбор content:// и file:// на Android

Короткий ответ: content:// — доступ через ContentProvider (работайте через InputStream/OutputStream), file:// — прямой путь в файловой системе и пригоден только для ваших файлов; чаще всего «реального» пути для content:// не существует, поэтому нужно читать через потоки или копировать файл в своё хранилище.

Чем отличаются content:// и file://

  • file:// — указывает на реальный файл, например /data/data//files/image.jpg. Подходит для файлов, которые вы создали и контролируете. Можно использовать java.io.File.
  • content:// — абстракция через ContentProvider (галерея, SAF, другие приложения). URI не раскрывает реальный путь; доступ осуществляется через ContentResolver (openInputStream/openOutputStream) и документные API.

Не пытайтесь массово конвертировать content:// в /storage/… — на современных Android это ненадёжно и часто невозможно из‑за scoped storage и облачных провайдеров.

Что делать, если у вас есть только content:// — практическое руководство

  1. Откройте поток:
    • InputStream in = context.getContentResolver().openInputStream(uri)
  2. Если нужно только прочитать/показать — работайте с потоком напрямую (BitmapFactory, парсеры, загрузка на сервер).
  3. Если библиотека требует «путь к файлу» или нужен постоянный доступ — скопируйте данные в файл в вашем пространстве:
    • создайте файл в context.getFilesDir() или context.getCacheDir() или в externalFilesDir(...) при необходимости;
    • OutputStream out = new FileOutputStream(destFile);
    • побайтово скопируйте данные из in в out, закройте потоки.
  4. Сохраняйте путь/URI так, как удобнее: для внутренних операций храните путь к собственной копии; для ссылки на источник — храните исходный content://.

Если сторонняя библиотека требует «реальный путь», скопируйте content:// в временный файл и передайте путь к копии. Это простое и надёжное решение.

Как правильно создавать и хранить файлы внутри приложения

  • Внутреннее хранилище (без разрешений): context.filesDir — стабильный путь, доступный только приложению.
  • Scoped external (Android/data//files): context.getExternalFilesDir(...) — подходит для крупных файлов, видны пользователю через проводник ограниченно.
  • Для временных задач используйте context.getCacheDir() или File.createTempFile(..., context.cacheDir).

Алгоритм:

  1. Выберите директорию (filesDir / cacheDir / externalFilesDir).
  2. Создайте файл через API (File(...) или openFileOutput).
  3. Пишите/читайте через потоки и обеспечьте корректное удаление временных файлов.

Частые ошибки

  • Ожидать, что content:// всегда можно преобразовать в /storage/… — приводит к сбоям.
  • Передавать file:// другому приложению без FileProvider — вызывает FileUriExposedException на новых Android.
  • Хранить большие файлы в памяти при копировании — используйте буфер (например, 8 KB) и стримы.
  • Не закрывать потоки — приводит к утечкам и коррумпированию файлов.

FAQ

  • Можно ли получить реальный путь из content://?
    Иногда можно (на старых устройствах или для конкретных провайдеров), но в целом полагаться на это нельзя. Лучше работать через ContentResolver и копирование.

  • Как безопасно шарить файл с другим приложением?
    Используйте FileProvider и выдавайте временные URI (content://) с grantUriPermission или Intent с флагами FLAG_GRANT_READ_URI_PERMISSION / FLAG_GRANT_WRITE_URI_PERMISSION.

  • Что делать, если библиотека принимает только File и не поддерживает InputStream?
    Скопируйте содержимое в файл в вашем пространстве и передайте путь к этой копии.

  • Нужно ли просить разрешения на чтение внешнего хранилища?
    Для доступа к своим директориям нет. Для общедоступных файлов на старых версиях может потребоваться READ_EXTERNAL_STORAGE; для современных Android используйте SAF или scoped storage.


Используйте потоковый доступ и копирование в своё хранилище как базовую стратегию: это совместимо с политиками безопасности Android и даёт стабильное поведение на разных устройствах и версиях ОС.