Как прикреплять и открывать несколько файлов в управляемых формах 1С?

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

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

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

Подходы к решению задачи

Мы проанализируем два основных подхода к прикреплению и открытию файлов:

  1. Интеграция подсистемы "Работа с файлами" из Библиотеки Стандартных Подсистем (БСП). Это наиболее комплексное и рекомендованное решение для большинства проектов.
  2. Реализация собственного механизма с использованием реквизита типа ХранилищеЗначений и функций для клиент-серверного взаимодействия с файлами. Этот вариант подходит, если интеграция БСП нежелательна или избыточна для ваших требований.

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

Если в вашей конфигурации уже используется БСП или вы готовы ее внедрить, то самым надежным и функциональным решением будет интеграция подсистемы "Работа с файлами". Эта подсистема разработана фирмой "1С" и предлагает готовый, проверенный временем механизм для всех операций с файлами.

Преимущества БСП:

Как работает подсистема "Работа с файлами" в БСП:

В БСП файлы хранятся в специализированных справочниках (например, "ПрисоединенныеФайлы"), которые связаны с объектами конфигурации (документами, справочниками), к которым они прикреплены. Сами данные файлов могут храниться либо непосредственно в базе данных (в виде ХранилищеЗначений), либо во внешних томах (на сетевых дисках), что настраивается администратором.

Общие шаги по внедрению подсистемы "Работа с файлами" из БСП:

  1. Включение подсистемы: Сначала мы включаем подсистему "РаботаСФайлами" в нашу конфигурацию.
  2. Настройка владельцев файлов: Мы указываем, к каким объектам нашей конфигурации могут быть прикреплены файлы. Для этого, как правило, в общих типах конфигурации мы включаем ссылки на необходимые документы или справочники (например, ДокументСсылка.МойДокумент).
  3. Создание или адаптация справочника файлов: Мы создаем или адаптируем справочник для хранения файлов, используя типовые структуры, предоставляемые БСП. Часто это подразумевает копирование уже имеющегося в БСП справочника "ПрисоединенныеФайлы" и его настройку под свои нужды, изменяя тип владельца.
  4. Встраивание команд в формы: Мы интегрируем в формы наших объектов (документов, справочников) стандартные команды и элементы для работы с файлами. БСП предоставляет готовые формы и команды, которые можно использовать, что значительно упрощает разработку.

Важный момент, касающийся БСП:

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

2. Ручная реализация с использованием ХранилищаЗначений

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

Хранение файлов в табличной части

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

  1. ИмяФайла (Тип: Строка) — для отображения полного имени файла пользователю (например, "Отчет.pdf").
  2. Расширение (Тип: Строка) — для определения типа файла при открытии (например, "pdf", "docx").
  3. Размер (Тип: Число) — для информации о размере файла в байтах.
  4. ДанныеФайла (Тип: ХранилищеЗначений) — этот реквизит будет хранить содержимое файла в бинарном виде непосредственно в базе данных. Это ключевой реквизит для хранения самих данных файла.

Процесс прикрепления файла:

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

  1. На клиентской стороне мы запросим у пользователя выбрать файл с диска.
  2. Затем мы передадим полный путь к выбранному файлу на сервер.
  3. На сервере мы прочитаем содержимое файла в двоичные данные и создадим из них новый объект ХранилищеЗначений.
  4. Полученное ХранилищеЗначений мы запишем в реквизит ДанныеФайла новой строки табличной части.

Рассмотрим пример кода для прикрепления файла:


// Пример на клиенте: Выбор файла и передача его на сервер для сохранения
&НаКлиенте
Процедура ПрикрепитьФайл()

    ДиалогВыбораФайла = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    ДиалогВыбораФайла.Заголовок = "Выберите файл для прикрепления";
    ДиалогВыбораФайла.МножественныйВыбор = Ложь; // Указываем, что можно выбрать только один файл

    Если ДиалогВыбораФайла.Выбрать() Тогда
        ПолноеИмяФайлаНаКлиенте = ДиалогВыбораФайла.ПолноеИмяФайла;
        ИмяФайла = ДиалогВыбораФайла.ИмяФайла;

        // Вызываем серверную процедуру для сохранения файла
        // В параметрах передаем полный путь к файлу на клиенте и его имя
        СохранитьФайлНаСервере(ПолноеИмяФайлаНаКлиенте, ИмяФайла);
    КонецЕсли;

КонецПроцедуры

// Пример на сервере: Сохранение файла в ХранилищеЗначений
&НаСервере
Процедура СохранитьФайлНаСервере(ПолноеИмяФайлаНаКлиенте, ИмяФайла)

    // Читаем файл с клиента в двоичные данные на сервере
    // Функция Новый ДвоичныеДанные(ПолноеИмяФайлаНаКлиенте) автоматически передает файл
    ДвоичныеДанные = Новый ДвоичныеДанные(ПолноеИмяФайлаНаКлиенте); 
    
    // Создаем ХранилищеЗначений из двоичных данных.
    // Можно использовать сжатие для экономии места в базе.
    Хранилище = Новый ХранилищеЗначений(ДвоичныеДанные, Новый СжатиеДанных(9)); 

    // Добавляем новую строку в нашу табличную часть
    // Предполагаем, что у нашей формы есть реквизит "Объект"
    // и у "Объекта" есть табличная часть с именем "ПрикрепленныеФайлы"
    НоваяСтрока = Объект.ПрикрепленныеФайлы.Добавить(); 
    НоваяСтрока.ИмяФайла = ИмяФайла;
    НоваяСтрока.Расширение = ОбщегоНазначенияКлиентСервер.ПолучитьРасширениеФайла(ИмяФайла); 
    НоваяСтрока.Размер = ДвоичныеДанные.Размер();
    НоваяСтрока.ДанныеФайла = Хранилище;

КонецПроцедуры

Открытие файлов из табличной части

Для открытия файла, сохраненного в ХранилищеЗначений, нам потребуется выполнить следующие шаги, поскольку файл из базы данных нельзя открыть напрямую:

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

Рассмотрим пример кода для открытия файла:


// Пример на клиенте: Обработчик события для открытия файла (например, по клику на имя файла)
&НаКлиенте
Процедура ПрикрепленныеФайлыИмяФайлаНажатие(Элемент, СтандартнаяОбработка)

    Если Элементы.ПрикрепленныеФайлы.ТекущиеДанные = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Получаем текущую строку табличной части, из которой хотим открыть файл
    СтрокаТЧ = Элементы.ПрикрепленныеФайлы.ТекущиеДанные;
    
    // Вызываем серверную функцию, чтобы получить путь к временному файлу на сервере
    // Файл будет извлечен из ХранилищаЗначений и временно сохранен на сервере
    ПутьКФайлуНаСервере = ПолучитьПутьКФайлуИзХранилищаНаСервере(СтрокаТЧ.ДанныеФайла, СтрокаТЧ.ИмяФайла);

    Если ПутьКФайлуНаСервере <> Неопределено Тогда
        // Подготавливаем описание оповещения. Это обратный вызов, 
        // который будет выполнен на клиенте после завершения получения файла.
        ОписаниеОповещения = Новый ОписаниеОповещения("ПослеПолученияФайла", ЭтотОбъект, ПутьКФайлуНаСервере);
        
        // Начинаем асинхронное получение файла с сервера на клиент.
        // Первый параметр - путь к файлу на сервере.
        // Второй параметр (Истина) - указание удалить временный файл на сервере после получения.
        // Третий параметр - наше оповещение.
        // Четвертый параметр - имя файла, под которым он будет сохранен на клиенте.
        НачатьПолучениеФайлаССервера(ПутьКФайлуНаСервере, Истина, ОписаниеОповещения, СтрокаТЧ.ИмяФайла);
    КонецЕсли;

КонецПроцедуры

// Процедура-обработчик после получения файла с сервера (выполняется на клиенте)
&НаКлиенте
Процедура ПослеПолученияФайла(ПолноеИмяФайлаНаКлиенте, ДополнительныеПараметры) Экспорт

    Если ПолноеИмяФайлаНаКлиенте <> Неопределено Тогда
        // Файл успешно загружен на клиент во временную папку. Теперь можем его открыть.
        ЗапуститьПриложение(ПолноеИмяФайлаНаКлиенте);
        
        // Важно: временный файл на клиенте необходимо удалить, чтобы не засорять диск.
        // Это можно сделать, например, при закрытии формы или через короткий промежуток времени.
        // Для демонстрации мы просто сообщаем о пути.
        // Сообщить("Файл сохранен на клиенте во временный файл: " + ПолноеИмяФайлаНаКлиенте);
        // Например, можно добавить вызов для удаления файла после задержки
        // СоздатьФоновоеЗаданиеДляУдаления(ПолноеИмяФайлаНаКлиенте); // Гипотетическая серверная функция
    Иначе
        Сообщить("Не удалось получить файл с сервера.");
    КонецЕсли;

КонецПроцедуры

// Серверная функция: извлечение файла из хранилища и сохранение его во временный файл на сервере
&НаСервере
Функция ПолучитьПутьКФайлуИзХранилищаНаСервере(ХранилищеФайла, ИмяФайла)

    Попытка
        ДвоичныеДанные = ХранилищеФайла.Получить(); // Извлекаем двоичные данные из ХранилищаЗначений
    Исключение
        Сообщить("Не удалось получить данные файла из хранилища: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;

    Если ДвоичныеДанные = Неопределено Тогда
        Сообщить("В хранилище нет данных.");
        Возврат Неопределено;
    КонецЕсли;

    // Генерируем уникальное имя для временного файла на сервере.
    // Это важно, чтобы избежать конфликтов при одновременной работе нескольких пользователей.
    ПолноеИмяВременногоФайлаНаСервере = ПолучитьКаталогВременныхФайлов() + УникальныйИдентификатор() + "_" + ИмяФайла;

    Попытка
        ДвоичныеДанные.Записать(ПолноеИмяВременногоФайлаНаСервере); // Сохраняем двоичные данные во временный файл
        Возврат ПолноеИмяВременногоФайлаНаСервере; // Возвращаем путь к этому временному файлу на сервере
    Исключение
        Сообщить("Ошибка при сохранении временного файла на сервере: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;

КонецФункции

Функции НачатьПолучениеФайлаССервера() и ЗапуститьПриложение():

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

Заключение

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

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

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

← На главную