Работа с файловой системой на мобильных устройствах существенно отличается от привычной работы в среде Windows. Разработчики часто сталкиваются с ситуацией, когда вместо понятного пути к файлу (например, /sdcard/documents/file.pdf) система выдает строку вида content://media/external/images/media/20. В этой статье мы подробно разберем, почему это происходит, как с этим работать в 1С и какие современные методы позволяют получить доступ к данным файла независимо от операционной системы (Android или iOS).
Начиная с Android 7.0 и особенно в более поздних версиях, политика безопасности Google (известная как Scoped Storage) ограничивает прямой доступ приложений к файловой системе. Когда вы вызываете диалог выбора файла, система не отдает приложению прямой путь, так как у приложения может не быть прав на чтение конкретной папки. Вместо этого система передает Content URI.
Проанализируем ситуацию: content:// — это не путь, а ссылка на ресурс внутри провайдера контента. Объект 1С Файл не может инициализироваться по такой ссылке напрямую, так как он ожидает физический адрес в файловой системе. Чтобы «вытащить» файл из этой ссылки, нам нужно либо использовать нативные средства платформы 1С, либо прибегнуть к помощи внешних компонент.
Если штатный диалог выбора файлов в вашей версии платформы работает нестабильно или не обеспечивает нужной фильтрации, мы можем воспользоваться механизмом ЗапускПриложенияМобильногоУстройства. Этот метод позволяет вызвать системный проводник Android напрямую.
Рассмотрим пример реализации кода для вызова выбора файла:
НовВз = Новый ЗапускПриложенияМобильногоУстройства;
НовВз.Действие = "android.intent.action.GET_CONTENT";
// Указываем фильтр типов файлов. "*/*" означает все типы.
// Если нужны только изображения, используйте "image/*"
НовВз.Тип = "*/*";
// Ограничиваем выбор только локальными данными (без облачных хранилищ)
НовВз.ДополнительныеДанные.Добавить("android.intent.extra.LOCAL_ONLY", Истина);
РезультатРаботы = НовВз.Запустить(Истина);
Если НЕ РезультатРаботы Тогда
Сообщить("Выбор файла отменен пользователем");
Возврат;
КонецЕсли;
// Получаем URI выбранного файла
URIФайла = НовВз.Данные;
После выполнения этого кода в переменной URIФайла окажется тот самый путь content://.... Чтобы превратить его в объект, с которым может работать 1С, потребуется «резолвинг» (преобразование).
Как отмечалось в обсуждении, одним из популярных способов решения является использование внешней компоненты Acode tools. Она содержит метод ПолучитьUriИзContent, который берет на себя всю низкоуровневую работу: открытие потока данных из Android ContentResolver, создание временной копии файла в песочнице 1С и возврат пути к этой копии.
Разберем, как выглядит логика обработки после получения URI:
// Предположим, компонента подключена и доступна через модуль УтилитыACODEКлиент
Если Найти(URIФайла, "content://") > 0 Тогда
// Получаем путь к временному файлу, который создаст компонента
ПутьКФайлу = УтилитыACODEКлиент.ПолучитьUriИзContent(URIФайла);
ВыбФайл = Новый Файл(ПутьКФайлу);
ИмяФайла = ВыбФайл.Имя;
Расширение = ВыбФайл.Расширение;
ПутьДляОбработки = ВыбФайл.ПолноеИмя;
Сообщить("Файл успешно преобразован: " + ИмяФайла);
Иначе
// Если это обычный путь, работаем стандартно
ВыбФайл = Новый Файл(URIФайла);
КонецЕсли;
Выясним, как современные версии платформы 1С (начиная с 8.3.15+) позволяют упростить этот процесс. Вместо того чтобы пытаться получить физический путь, нам следует работать с содержимым файла. Платформа 1С во многих случаях умеет сама считывать данные из content:// URI, если передать его в конструктор ДвоичныеДанные.
Рассмотрим шаги по реализации этого метода:
ДвоичныеДанные, передавая URI вместо пути.
// Попробуем прочитать данные напрямую из URI
Попытка
ДанныеФайла = Новый ДвоичныеДанные(URIФайла);
// Формируем путь во временном каталоге мобильного устройства
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(".tmp");
ДанныеФайла.Записать(ИмяВременногоФайла);
// Теперь у нас есть реальный физический путь к копии файла
ВыбранныйФайл = Новый Файл(ИмяВременногоФайла);
Сообщить("Файл готов к работе, размер: " + ВыбранныйФайл.Размер());
Исключение
Сообщить("Не удалось прочитать файл напрямую из URI: " + ОписаниеОшибки());
КонецПопытки;
Проанализируем ситуацию с устройствами Apple. В iOS нет понятия content://, там используется строгая «песочница». Когда пользователь выбирает файл через системный диалог, iOS копирует этот файл в специальную папку Inbox внутри контейнера вашего приложения. В этом случае 1С сразу получает путь, к которому имеет доступ. Однако важно помнить, что эти файлы временные, и их нужно перемещать в рабочие каталоги, если они планируются к длительному хранению.
В ходе обсуждения упоминалась ошибка __A58268974A5D44E5__ при открытии диалога. Это специфический баг релизов платформы 8.3.10. Если вы столкнулись с подобным, проанализируйте следующие рекомендации:
.apk файла — частая причина «падения» нативных диалогов.*/* или выбор «Просто файлы» в левом меню Android может помочь обойти баг.Для обеспечения кроссплатформенности и стабильности работы мобильного приложения 1С, придерживайтесь следующих правил:
КаталогВременныхФайлов()) перед обработкой.НачатьПомещениеФайлаНаСервер, которые скрывают сложность работы с провайдерами контента.GET_CONTENT, будьте готовы к тому, что на разных прошивках Android (MIUI, Samsung OneUI) поведение системного проводника может отличаться.Таким образом, правильная стратегия заключается не в поиске пути, а в получении потока данных и его локальном сохранении.