В процессе разработки на платформе 1С:Предприятие 8.3 часто возникает задача по загрузке внешних файлов (документов, сертификатов, архивов) в базу данных. Типичный сценарий: пользователь выбирает файл на диске, и этот файл должен быть сохранен в реквизит справочника или документа с типом ХранилищеЗначения. Однако начинающие разработчики часто сталкиваются с тем, что файл после сохранения оказывается «битым» или не открывается. Проанализируем ситуацию, выясним причины ошибок и разберем правильный алгоритм реализации этой задачи.
Рассмотрим распространенную ошибку: попытку считать файл, превратить его в строку и передать в реквизит формы для последующей записи на сервере. Когда мы пытаемся записать «сырые» байты файла в обычную текстовую строку 1С (которая использует кодировку Unicode), происходит необратимое искажение данных. Некоторые последовательности байтов интерпретируются как спецсимволы, другие просто теряются. В итоге, при обратном преобразовании строки в ДвоичныеДанные, мы получаем массив байтов, который уже не соответствует исходному файлу.
Важный момент: если по каким-то специфическим причинам вам абсолютно необходимо передать файл именно через текст, используйте кодирование Base64. В 1С для этого предусмотрены функции Base64Строка(ДвоичныеДанные) и Base64Значение(Строка). Это гарантирует целостность, хотя и увеличивает объем данных на 33%. Однако для стандартных задач загрузки файлов в 1С существует гораздо более эффективный механизм — Временное хранилище.
Чтобы избежать искажения данных и обеспечить корректную работу в веб-клиенте и тонком клиенте, мы будем использовать связку из временного хранилища и реквизита формы для хранения адреса. Разберем этот процесс по шагам.
Шаг 1. Подготовка формы. Создадим на форме реквизит АдресФайлаВоВременномХранилище с типом Строка. В нем мы будем временно держать ссылку на данные, пока пользователь не нажмет кнопку «Записать».
Шаг 2. Загрузка файла на стороне клиента. Проанализируем код, который инициирует выбор файла и его передачу на сервер. Мы будем использовать асинхронный метод НачатьПомещениеФайлаНаСервер, так как это стандарт для современных конфигураций.
&НаКлиенте
Процедура КомандаВыбратьФайл(Команда)
// Описываем оповещение, которое сработает после того, как файл попадет на сервер
Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
// Начинаем процесс выбора и загрузки
// Параметр УникальныйИдентификатор формы крайне важен — он определяет время жизни файла
НачатьПомещениеФайлаНаСервер(Оповещение, , , , , ЭтаФорма.УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(ОписаниеПомещенногоФайла, ДополнительныеПараметры) Экспорт
Если ОписаниеПомещенногоФайла <> Неопределено Тогда
// Сохраняем адрес во временном хранилище в реквизит формы
АдресФайлаВоВременномХранилище = ОписаниеПомещенногоФайла.Адрес;
Модифицированность = Истина;
КонецЕсли;
КонецПроцедуры
Шаг 3. Запись в объект на сервере. Теперь рассмотрим, как перенести данные из временного хранилища в реквизит объекта при записи формы. Это происходит в обработчике ПередЗаписьюНаСервере.
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
// Проверяем, был ли выбран новый файл (есть ли адрес)
Если ЭтоАдресВременногоХранилища(АдресФайлаВоВременномХранилище) Тогда
// Получаем двоичные данные по адресу
ДвоичныеДанныеФайла = ПолучитьИзВременногоХранилища(АдресФайлаВоВременномХранилище);
// Помещаем данные в ХранилищеЗначения.
// По умолчанию используется среднее сжатие данных.
ТекущийОбъект.ПолеФайл = Новый ХранилищеЗначения(ДвоичныеДанныеФайла);
// После записи адрес можно очистить, чтобы не занимать память
АдресФайлаВоВременномХранилище = "";
КонецЕсли;
КонецПроцедуры
Рассмотрим подробнее использование УникальногоИдентификатора формы. Если при вызове метода НачатьПомещениеФайлаНаСервер не указать идентификатор формы, данные во временном хранилище будут удалены сразу после завершения первого же серверного вызова. Привязав данные к форме, мы гарантируем, что файл будет доступен до тех пор, пока пользователь не закроет окно. Это критично, если пользователь сначала выбрал файл, а нажал «Записать» только через некоторое время.
Также проанализируем ситуацию со сжатием данных. Конструктор Новый ХранилищеЗначения позволяет управлять уровнем сжатия:
Новый СжатиеДанных(0).Важно помнить, что объект ХранилищеЗначения сохраняет только «голые» байты. Он не знает, как назывался файл, какое у него было расширение и каков его размер. Для полноценной работы системы в справочник необходимо добавить дополнительные реквизиты:
ИмяФайла (Строка) — для корректного предложения имени при выгрузке.Расширение (Строка) — чтобы система знала, какой программой открывать файл.РазмерФайла (Число) — чтобы отображать информацию в списке, не «поднимая» тяжелые данные из хранилища.В новых версиях платформы мы можем сделать код еще более лаконичным, используя ключевое слово Ждать. Это избавляет нас от необходимости создавать отдельные процедуры для оповещений.
&НаКлиенте
Асинхронная Процедура ВыбратьИЗагрузитьФайл()
Результат = Ждать ПоместитьФайлНаСерверАсинхронно(,,, ЭтаФорма.УникальныйИдентификатор);
Если Результат <> Неопределено Тогда
АдресФайлаВоВременномХранилище = Результат.Адрес;
Объект.ИмяФайла = Результат.СсылкаНаФайл.Имя;
КонецЕсли;
КонецПроцедуры
Таким образом, мы обеспечиваем надежное сохранение файлов, которое корректно работает во всех типах клиентов и не приводит к порче данных. Использование временного хранилища — это наиболее производительный и безопасный способ передачи бинарных данных между клиентом и сервером в 1С.