Как правильно записать файл в реквизит документа 1С и избежать ошибок передачи данных с клиента на сервер?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

В практике разработки на платформе 1С:Предприятие часто возникает задача прикрепления файлов (сканов, договоров, изображений) к документам. Казалось бы, задача тривиальная: создать реквизит типа ХранилищеЗначения и записать туда файл. Однако, при реализации этого механизма, особенно в клиент-серверной архитектуре, разработчики сталкиваются с ошибками доступа к файловой системе или "зависанием" отладки. Кроме того, существует важный вопрос архитектуры базы данных: стоит ли вообще хранить файлы внутри самих документов?

В этой статье мы подробно разберем техническую реализацию передачи файла с клиента на сервер и рассмотрим, почему опытные архитекторы 1С настоятельно не рекомендуют хранить бинарные данные (BLOB) непосредственно в реквизитах транзакционных объектов.

Разбор типичной ошибки: почему сервер "не видит" файл

Рассмотрим ситуацию, с которой столкнулся автор темы. В расширении конфигурации был добавлен реквизит Файл с типом ХранилищеЗначения. Задача — выбрать файл на компьютере пользователя и сохранить его в этот реквизит.

Изначальный код выглядел следующим образом:


&НаСервере    
Процедура ПослеВыбораФайла(Результат, ДополнительныеПараметры) 
    // В Результат[0] содержится путь к файлу на диске клиента, например "C:\Users\User\Desktop\File.pdf"
    ДД = Новый ДвоичныеДанные(Результат[0]);   
    Объект.Файл = Новый ХранилищеЗначения(ДД); 
КонецПроцедуры

При выполнении этого кода возникают ошибки, такие как "Ошибка при вызове конструктора (ДвоичныеДанные)", или отладчик просто останавливается.

В чем причина ошибки?

  1. Метод выполняется в контексте &НаСервере.
  2. Переменная Результат[0] содержит локальный путь к файлу на компьютере пользователя (клиента).
  3. Сервер 1С физически находится на другой машине (или в другом процессе) и не имеет доступа к диску C:\ компьютера пользователя. Для сервера этот путь либо не существует, либо указывает на локальный диск самого сервера, где такого файла нет.

Техническое решение: Использование Временного хранилища

Чтобы передать файл с клиента на сервер, необходимо использовать механизм Временного хранилища. Алгоритм действий должен быть следующим:

  1. На клиенте считываем файл в ДвоичныеДанные.
  2. На клиенте помещаем эти данные во временное хранилище, получая адрес (строку-ссылку).
  3. Передаем этот адрес на сервер.
  4. На сервере получаем данные по адресу и сохраняем их в ХранилищеЗначения.

Разберем правильную реализацию кода по шагам.

Шаг 1. Клиентская часть

Сначала обрабатываем выбор файла на клиенте. Создаем двоичные данные и помещаем их во временное хранилище. Обратите внимание: для привязки к форме (чтобы хранилище очистилось после закрытия формы) можно использовать Уникальный Идентификатор формы, но для разовой передачи часто создают новый идентификатор или передают без него.


&НаКлиенте
Процедура ПослеВыбораФайла(Результат, ДополнительныеПараметры) ЭКСПОРТ
    
    Если Результат = Неопределено Тогда
        Возврат;
    КонецЕсли;

    ПолноеИмяФайла = Результат[0];
    
    // 1. Считываем файл в двоичные данные прямо на клиенте
    ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПолноеИмяФайла);
    
    // 2. Помещаем во временное хранилище
    // Новый УникальныйИдентификатор() гарантирует уникальность адреса
    АдресВХ = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, Новый УникальныйИдентификатор());
    
    // 3. Вызываем серверную процедуру, передавая туда АДРЕС, а не путь к файлу
    СохранитьФайлНаСервере(АдресВХ);
    
КонецПроцедуры

Шаг 2. Серверная часть

Теперь на стороне сервера мы можем "достать" переданный файл из оперативной памяти временного хранилища и записать его в реквизит объекта.


&НаСервере
Процедура СохранитьФайлНаСервере(АдресВХ)
    
    // 4. Получаем данные из временного хранилища
    ДД = ПолучитьИзВременногоХранилища(АдресВХ);
    
    Если ДД <> Неопределено Тогда
        // 5. Записываем в реквизит типа ХранилищеЗначения
        // Рекомендуется использовать сжатие для экономии места
        Объект.Файл = Новый ХранилищеЗначения(ДД, Новый СжатиеДанных(9));
    КонецЕсли;
    
КонецПроцедуры

Такой подход гарантирует, что файл будет успешно передан с машины пользователя на сервер приложений, независимо от сетевой инфраструктуры.

Архитектурный аспект: Почему не стоит хранить файлы в документах?

Хотя технически мы решили задачу, опытные разработчики в обсуждении настоятельно рекомендовали не хранить файлы (BLOB-данные) непосредственно в реквизитах документа. Давайте проанализируем причины этой рекомендации.

1. Производительность чтения (Проблема "Бетонных блоков")

В платформе 1С чтение объекта данных (справочника или документа) происходит целиком. Когда вы обращаетесь к документу через ДокументОбъект (например, при проведении, открытии формы или программной обработке), система считывает все его реквизиты в оперативную память.

Если в документе хранится файл размером 5-10 МБ, то при каждом обращении к объекту сервер будет вынужден считывать и эти мегабайты, даже если они вам в данный момент не нужны. Это создает колоссальную нагрузку на сеть и память сервера, замедляя работу всей системы. Как метко заметили в обсуждении: "Это как если бы в библиотеке к каждой книге был привязан бетонный блок, и чтобы почитать книгу, вам приходилось бы тащить его с собой".

2. Блокировки транзакций

Запись большого объема данных занимает время. Пока документ с большим файлом записывается в базу данных, таблица документов может быть заблокирована. Это мешает параллельной работе других пользователей, которые в этот момент могут просто изменять статус или проводить другие документы того же типа.

3. Разрастание базы данных

Хранение бинарных данных внутри .1CD файла (для файловой версии) или в таблицах SQL приводит к быстрому росту размера базы. Это усложняет создание резервных копий (backup) и выполнение регламентных операций.

Правильная архитектура: Справочник "ПрисоединенныеФайлы"

Штатным и наиболее правильным решением является вынос хранения файлов в отдельную сущность.

Как это реализовать:

  1. Создайте новый справочник, например, ФайлыДокументов или используйте подсистему БСП "Присоединенные файлы".
  2. В этом справочнике создайте реквизит ВладелецФайла (ссылка на ваш Документ) и реквизит ДанныеФайла (ХранилищеЗначения).
  3. Еще лучше — использовать Регистр Сведений для хранения двоичных данных, а справочник оставить для хранения метаданных (имя, автор, дата).

Преимущества такого подхода:

Вывод

Для записи файла, выбранного на клиенте, в базу данных 1С необходимо использовать Временное хранилище как промежуточный буфер обмена. Прямая передача пути к файлу в конструктор ДвоичныеДанные на сервере невозможна.

Однако, прежде чем реализовывать сохранение файла в реквизит самого документа, рассмотрите вариант использования отдельного справочника или регистра. Это убережет вашу систему от проблем с производительностью в будущем и является стандартом разработки в среде 1С:Предприятие.

← На главную