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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

В этой статье мы подробно рассмотрим несколько способов решения данной задачи: от классического платформенного механизма клиент-серверного взаимодействия до использования возможностей Библиотеки стандартных подсистем (БСП) и оптимизации работы с объемами данных.

Особенности архитектуры клиент-сервер при работе с хранилищем

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

Для решения этой проблемы мы должны использовать следующую схему:

  1. На сервере извлечь данные из хранилища с помощью метода Получить().
  2. Поместить полученный объект (обычно это ДвоичныеДанные) во временное хранилище с помощью функции ПоместитьВоВременноеХранилище().
  3. Передать полученный адрес временного хранилища на клиент.
  4. На клиенте сохранить данные на диск или использовать их для отображения на форме.

Рассмотрим по шагам практическую реализацию каждого из доступных способов.

Способ 1. Получение файла через временное хранилище (Платформенный метод)

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

Разберем пример кода, реализующий этот алгоритм:


&НаКлиенте
Процедура ПолучитьФайлИзОбъекта(СсылкаНаОбъект)
    
    // Шаг 1: Получаем адрес во временном хранилище через серверный вызов
    АдресВременногоХранилища = ПолучитьАдресФайлаНаСервере(СсылкаНаОбъект);
    
    Если ЗначениеЗаполнено(АдресВременногоХранилища) Тогда
        
        // Шаг 2: Указываем имя, под которым файл должен быть сохранен у пользователя
        ИмяФайлаНаДиске = "C:\Temp\ИмпортированныйФайл.pdf";
        
        // Шаг 3: Используем современный асинхронный метод для скачивания файла без блокировки интерфейса
        ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПолученияФайлаССервера", ЭтотОбъект);
        НачатьПолучениеФайлаССервера(ОписаниеОповещения, АдресВременногоХранилища, ИмяФайлаНаДиске);
        
    Иначе
        ПоказатьПредупреждение(, "В указанном объекте файл отсутствует!");
    КонецЕсли;
    
КонецПроцедуры

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

&НаКлиенте
Процедура ПослеПолученияФайлаССервера(ПолученныйФайл, ДополнительныеПараметры) Экспорт
    ПоказатьПредупреждение(, "Файл успешно сохранен на локальный диск!");
КонецПроцедуры

Обратите внимание, что начиная с версии платформы 8.3.15, использование синхронного метода ПолучитьФайл() крайне не рекомендуется, так как он не работает в веб-клиенте без установки специального расширения. Вместо него мы применили асинхронный метод НачатьПолучениеФайлаССервера().

Способ 2. Использование навигационных ссылок (Без написания серверного кода)

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

Рассмотрим, как это работает на практике:


&НаКлиенте
Процедура ПолучитьФайлПоНавигационнойСсылке(СсылкаНаОбъект)
    
    // Получаем прямую ссылку на реквизит "ХранилищеФайла" объекта базы данных
    АдресФайла = ПолучитьНавигационнуюСсылку(СсылкаНаОбъект, "ХранилищеФайла");
    
    Если ЗначениеЗаполнено(АдресФайла) Тогда
        
        // Инициируем скачивание файла напрямую с сервера по навигационной ссылке
        ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПолученияФайлаССервера", ЭтотОбъект);
        НачатьПолучениеФайлаССервера(ОписаниеОповещения, АдресФайла, "C:\Temp\ФайлИзНавигационнойСсылки.xlsx");
        
    Иначе
        ПоказатьПредупреждение(, "Не удалось сформировать ссылку на файл.");
    КонецЕсли;
    
КонецПроцедуры

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

Способ 3. Получение файлов в типовых конфигурациях с БСП

В современных типовых конфигурациях (таких как 1С:Управление торговлей 11, ERP, Комплексная автоматизация, Бухгалтерия предприятия) файлы редко хранятся в реквизитах основных документов. Обычно для этого используется подсистема БСП «Работа с файлами». Файлы привязываются к специальным подчиненным справочникам (например, НоменклатураПрисоединенныеФайлы).

В этом случае БСП берет на себя всю сложную логику определения места хранения файла (база данных, тома на диске в сетевой папке или внешнее облачное хранилище S3). Для разработчика этот процесс полностью скрыт за одной универсальной функцией.

Разберем, как получить двоичные данные файла, имея ссылку на элемент присоединенного файла:


&НаСервере
Процедура ПолучитьФайлИзБСП(ПрисоединенныйФайлСсылка, ПутьНаДиске)
    
    // Проверяем, заполнена ли ссылка на присоединенный файл
    Если ЗначениеЗаполнено(ПрисоединенныйФайлСсылка) Тогда
        
        // Метод БСП сам определит, где лежит файл (в базе или в томе на диске) 
        // и вернет готовый объект ДвоичныеДанные
        ДвоичныеДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(ПрисоединенныйФайлСсылка);
        
        // Записываем полученные двоичные данные в файл на сервере
        ДвоичныеДанныеФайла.Записать(ПутьНаДиске);
        
    КонецЕсли;
    
КонецПроцедуры

Если вам нужно не просто сохранить файл на сервере, а передать его на клиент для работы пользователя, вы можете скомбинировать этот метод с помещением во временное хранилище:


&НаСервере
Функция ПолучитьАдресФайлаБСП(ПрисоединенныйФайлСсылка)
    
    ДвоичныеДанныеФайла = РаботаСФайлами.ДвоичныеДанныеФайла(ПрисоединенныйФайлСсылка);
    Возврат ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);
    
КонецФункции

Оптимизация: определение размера файла без его полного скачивания

Иногда перед получением самого файла нам требуется узнать его размер (например, чтобы предупредить пользователя о скачивании слишком большого файла). В интернет-источниках часто предлагают преобразовывать данные хранилища в строку Base64 и высчитывать длину по математическим формулам. Этот способ крайне неэффективен, так как кодирование больших объемов данных в строку сильно нагружает оперативную память сервера.

Правильный и быстрый способ определения размера данных заключается в использовании встроенного свойства Размер у объекта ДвоичныеДанные. Выясним, как это выглядит на практике:


&НаСервереБезКонтекста
Функция ПолучитьРазмерФайлаВБайтах(ХранилищеЗначения)
    
    ДанныеХранилища = ХранилищеЗначения.Получить();
    
    Если ДанныеХранилища = Неопределено Тогда
        Возврат 0;
    КонецЕсли;
    
    // Если внутри хранилища лежат стандартные ДвоичныеДанные
    Если ТипЗнч(ДанныеХранилища) = Тип("ДвоичныеДанные") Тогда
        Возврат ДанныеХранилища.Размер; // Возвращает точный размер в байтах мгновенно
    КонецЕсли;
    
    // Если внутри хранилища лежит Картинка
    Если ТипЗнч(ДанныеХранилища) = Тип("Картинка") Тогда
        Возврат ДанныеХранилища.ПолучитьДвоичныеДанные().Размер;
    КонецЕсли;
    
    // Если внутри хранилища обычная строка
    Если ТипЗнч(ДанныеХранилища) = Тип("Строка") Тогда
        Возврат СтрДлина(ДанныеХранилища);
    КонецЕсли;
    
    Возврат 0;
    
КонецФункции

Использование свойства Размер позволяет мгновенно получать точные характеристики файла без накладных расходов на конвертацию форматов данных.

← На главную