При разработке мобильных приложений на платформе 1С:Предприятие часто возникает задача программной отправки электронных писем с вложениями (например, счетами в формате PDF или отчетами в Excel). Однако, начиная с версии Android 7.0 (API level 24), разработчики сталкиваются с серьезным препятствием: приложение аварийно завершается с ошибкой FileUriExposedException. В этой статье мы подробно разберем, почему это происходит и какими способами можно реализовать корректную отправку файлов на современных версиях Android.
Разберем ситуацию: раньше для передачи файла во внешнее приложение (например, почтовый клиент Gmail) достаточно было сформировать путь по схеме file:// и передать его через механизм Intent. Начиная с Android 7.0, Google ужесточил правила безопасности. Теперь передача «голых» путей к файлам во внутреннем хранилище приложения запрещена, так как принимающее приложение может не иметь прав доступа к этой папке.
Вместо схемы file:// система требует использовать схему content://. Для этого в Android используется специальный механизм — FileProvider. Он выступает посредником, который предоставляет временный доступ к файлу другому приложению. Проанализируем, как эти изменения отражаются на разработке в 1С и какие инструменты у нас есть.
Наиболее простым и стабильным способом решения проблемы является использование встроенного объекта 1С СредстваПочты. Платформа 1С в своих современных сборках (начиная с 8.3.12 и выше) берет на себя всю сложную работу по взаимодействию с Android API. Она сама упаковывает путь к файлу в формат content://, инициализирует FileProvider и выставляет необходимые флаги разрешений.
Рассмотрим пример кода, который позволит нам отправить письмо с вложением без необходимости вручную прописывать Intent:
// Создаем объект сообщения
Сообщение = Новый СообщениеПочты;
Сообщение.Тема = "Тема вашего письма";
Сообщение.Текст = "Текст сообщения здесь";
Сообщение.Получатели.Добавить("example@mail.com");
// Указываем путь к нашему PDF-файлу
ПутьКФайлу = DocumentsDir() + "/Invoice.pdf";
Вложение = Сообщение.Вложения.Добавить(ПутьКФайлу);
Вложение.Наименование = "Invoice.pdf";
// Вызываем системный диалог отправки
СредстваПочты.Послать(Сообщение, СпособОтправкиПочты.ИспользоватьПочтовыйКлиент);
Важный момент: Для того чтобы этот метод работал корректно, убедитесь, что вы используете актуальную версию сборщика мобильных приложений. Платформа должна автоматически прописать в манифест AndroidManifest.xml необходимые разрешения и описание провайдера файлов.
Если ваша задача не ограничивается только почтой, а вы хотите дать пользователю выбор (отправить через почту, Telegram, WhatsApp или сохранить на диск), наиболее современным подходом будет использование объекта ПоказатьПлатформенныйРесурсОбмена. Это аналог системной кнопки «Поделиться» (Sharing).
Разберем, как это работает:
content://.Пример реализации:
ПутьКФайлу = DocumentsDir() + "/Report.pdf";
ФайлОбмена = Новый Файл(ПутьКФайлу);
Если ФайлОбмена.Существует() Тогда
ОписаниеРесурса = Новый ОписаниеПередаваемогоФайла(ПутьКФайлу);
МассивРесурсов = Новый Массив;
МассивРесурсов.Добавить(ОписаниеРесурса);
СредстваМультимедиа.ПоказатьПлатформенныйРесурсОбмена(МассивРесурсов);
КонецЕсли;
Этот метод считается самым «правильным» с точки зрения гайдлайнов Android, так как он минимизирует риски блокировок со стороны системы безопасности и не требует от разработчика 1С знаний о внутреннем устройстве URI провайдеров.
В некоторых случаях (например, если нужно использовать специфические параметры стороннего приложения) разработчики пытаются использовать MobileDeviceApplicationRun (ЗапускПриложенияМобильногоУстройства). Как мы выяснили из обсуждения, именно здесь возникает FileUriExposedException при использовании file://.
Если вы все же решили идти по этому пути, вам необходимо знать, что в мобильной платформе 1С уже встроен стандартный FileProvider. Чтобы сформировать URI вручную, вам нужно знать идентификатор пакета вашего приложения (Package ID). Обычно путь к файлу через провайдер выглядит так:
content://[ИдентификаторПакета].fileprovider/external_storage_root/[ПутьКФайлу]
Однако, проанализируем ситуацию: ручное формирование таких ссылок крайне затруднительно, так как структура XML-описания путей внутри платформы может меняться от версии к версии. Если вы используете старый код, как в сообщении автора темы:
MDARun = New MobileDeviceApplicationRun;
MDARun.Action = "android.intent.action.SEND";
MDARun.AdditionalData.Add("android.intent.extra.STREAM", "file://" + FilePath, "Uri");
То на Android 7+ он гарантированно не будет работать. Единственный вариант заставить работать MobileDeviceApplicationRun с файлами — это использовать метод ЗапуститьПриложение (LaunchApplication) в новых версиях платформы, передавая туда объект типа Файл. В этом случае 1С сама попытается выполнить преобразование схемы.
При работе с внешними приложениями на Android крайне важно учитывать два фактора:
Downloads) нужно запрашивать разрешение в явном виде у пользователя в момент выполнения операции. В 1С это делается через метод СредстваМультимедиа.ЗапроситьРазрешение(). Однако, если вы пишете файлы в DocumentsDir() (внутренняя папка приложения), дополнительные разрешения обычно не требуются, но доступ к ним через file:// извне закрыт.application/pdf. Если передать универсальный application/octet-stream, некоторые клиенты (например, Outlook) могут не распознать файл или отобразить его как поврежденный.Подведем итог нашему разбору. Если вы столкнулись с ошибкой FileUriExposedException в мобильном приложении 1С:
file:// в Intent.СредстваПочты для отправки писем — это самый надежный способ.ПоказатьПлатформенныйРесурсОбмена для универсальной передачи файлов в любые мессенджеры и клиенты.FileProvider.Следуя этим правилам, вы обеспечите стабильную работу вашего мобильного приложения на всех версиях Android, начиная от старых пятых версий и заканчивая современными Android 13/14.