При разработке функциональности для мобильного клиента 1С часто возникает задача передачи файлов (фотографий, документов, вложений) с мобильного устройства на основной сервер базы данных. Однако стандартный подход, привычный для настольного тонкого клиента через НачатьПомещениеФайла и объект ДиалогВыбораФайла, на мобильных устройствах под управлением Android может приводить к ошибкам.
Проанализируем типичную ситуацию: пользователь выбирает файл в системном диалоге мобильного устройства, но вместо привычного пути к файлу в операционной системе возвращается ссылка вида content://media/external/images/media/20. При этом файл не отправляется на сервер, либо код падает при попытке разобрать имя и расширение через объект Файл. Разберем причины такого поведения и рассмотрим надежные способы решения задачи.
В операционной системе Android (в частности, в рамках модели Scoped Storage) системный проводник или галерея возвращают не абсолютный путь файловой системы (например, /storage/emulated/0/...), а так называемый Content URI. Попытка инициализировать стандартный объект 1С конструкцией Новый Файл(ПутьСФлешки) по такой ссылке приводит к ошибке: методы парсинга строки пути не находят расширения (свойство Расширение возвращает пустую строку, а Имя содержит лишь цифровой идентификатор контента).
Кроме того, устаревший метод НачатьПомещениеФайла не всегда корректно взаимодействует с Android ContentResolver для открытия прямого потока байтов, из-за чего передача данных прерывается либо возвращает признак неудачного завершения (Результат = Ложь).
Рассмотрим вариант решения, предложенный на практике. Если на мобильном устройстве выбирается графический файл, мы можем явно получить его двоичные данные на стороне клиента, поместить их во временное хранилище и затем передать полученный адрес на сервер для сохранения.
Разберем по шагам реализацию этого механизма:
Картинка по выбранному локальному ресурсу и извлечем ДвоичныеДанные.Посмотрим на пример клиентского кода:
&НаКлиенте
Функция ВыбратьИПоместитьФайлВоВременноеХранилище()
ВыборФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
#Если МобильноеПриложениеКлиент Тогда
ВыборФайла.Каталог = КаталогБиблиотекиМобильногоУстройства(ПредопределенноеЗначение("ТипКаталогаБиблиотекиМобильногоУстройства.Картинки"));
#КонецЕсли
Если ВыборФайла.Выбрать() Тогда
// Создаем картинку по ссылке на ресурс устройства
ВыбранноеФото = Новый Картинка(ВыборФайла.ПолноеИмяФайла, Ложь);
ТекущееФото = ВыбранноеФото.ПолучитьДвоичныеДанные();
// Помещаем двоичные данные во временное хранилище
СсылкаНаХранилище = ПоместитьВоВременноеХранилище(ТекущееФото, УникальныйИдентификатор);
Иначе
СсылкаНаХранилище = "";
КонецЕсли;
Возврат СсылкаНаХранилище;
КонецФункции
Для сохранения файла на стороне сервера используем следующую функцию:
&НаСервере
Функция СохранитьФайлНаСервере(Знач АдресВХранилище, Знач Расширение)
Если ПустаяСтрока(АдресВХранилище) Тогда
Возврат "";
КонецЕсли;
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресВХранилище);
ПутьНаСервере = "C:\FileStorage\uploaded_file" + Расширение;
ДвоичныеДанныеФайла.Записать(ПутьНаСервере);
// Очищаем временное хранилище после сохранения
УдалитьИзВременногоХранилища(АдресВХранилище);
Возврат ПутьНаСервере;
КонецФункции
Рассмотрим современный стандарт разработки в платформе 1С:Предприятие 8.3. Начиная с версии 8.3.15, вместо устаревшей функции НачатьПомещениеФайла рекомендуется использовать метод НачатьПомещениеФайлаНаСервер, а в более поздних релизах — асинхронную конструкцию ПоместитьФайлНаСерверАсинх.
Преимущество этого подхода заключается в том, что платформа внутри мобильного клиента самостоятельно взаимодействует с системными провайдерами контента Android. При этом возвращаемый объект типа ОписаниеПомещенногоФайла автоматически заполняет валидное имя, расширение и помещает данные во временное хранилище.
Посмотрим на пример использования асинхронного метода:
&НаКлиенте
Асинх Процедура ЗагрузитьФайлНаСерверАсинх(Команда)
ПараметрыДиалога = Новый ПараметрыДиалогаПомещенияФайлов;
ПараметрыДиалога.Заголовок = "Выберите файл для загрузки";
ПараметрыДиалога.МножественныйВыбор = Ложь;
// Вызываем асинхронный метод помещения файла
ПомещенныйФайл = Ждать ПоместитьФайлНаСерверАсинх(,,, ПараметрыДиалога, УникальныйИдентификатор);
Если ПомещенныйФайл <> Неопределено Тогда
АдресВХранилище = ПомещенныйФайл.Адрес;
ИмяФайла = ПомещенныйФайл.СсылкаНаФайл.Имя;
Расширение = ПомещенныйФайл.СсылкаНаФайл.Расширение;
// Передаем данные на сервер
СохранитьФайлВБазеНаСервере(АдресВХранилище, ИмяФайла, Расширение);
КонецЕсли;
КонецПроцедуры
Если задача заключается исключительно в получении снимков с камеры или файлов из галереи мобильного устройства, эффективнее использовать глобальный объект СредстваМультимедиа. Он разработан специально для мобильных платформ и мобильных клиентов, что исключает конфликт с файловыми путями.
Проанализируем работу с объектом СредстваМультимедиа:
СделатьФотоснимок или выбор мультимедиа возвращает объект ДанныеМультимедиа.ПолучитьДвоичныеДанные(), а также доступно свойство РасширениеФайла.Важное замечание: При сборке собственного мобильного клиента в виде мобильного приложения убедитесь, что в конфигураторе в свойствах мобильного приложения установлены разрешения на использование мультимедиа и чтения внешнего хранилища, иначе операционная система заблокирует доступ к файлам.