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

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

При разработке самописных конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача организовать хранение различных файлов (документов Word, картинок, PDF) непосредственно в базе данных. Одной из наиболее распространенных проблем является ошибка получения навигационной ссылки при попытке открыть файл. Рассмотрим детально, как правильно реализовать этот механизм, разберем типичные ошибки и напишем надежный код для загрузки и выгрузки данных.

Почему возникает ошибка навигационной ссылки?

Часто разработчики пытаются использовать метод ПолучитьНавигационнуюСсылку для реквизита с типом ХранилищеЗначения. Однако стоит учитывать, что этот метод работает стабильно только тогда, когда реквизит напрямую привязан к форме или используется специфический формат пути. В случае с присоединенными файлами гораздо надежнее использовать работу с двоичными данными. Проанализируем ситуацию: когда мы помещаем файл в хранилище, платформа упаковывает его в специальный объект. Для его корректного извлечения на стороне клиента нам нужно сначала получить эти данные на сервере, передать их на клиент и записать в файл на диске.

Шаг 1. Выбор и загрузка файла на сервер

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


&НаКлиенте
Процедура ЗагрузитьДокумент(Команда)
    // Описываем оповещение, которое сработает после выбора файла
    Оповещение = Новый ОписаниеОповещения("ОбработатьВыборФайла", ЭтотОбъект);
    
    // Параметр Истина включает интерактивный выбор файла
    НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры

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


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

Шаг 2. Сохранение файла в справочнике (Сервер)

На сервере нам необходимо создать элемент справочника и упаковать двоичные данные в ХранилищеЗначения. Рекомендуется использовать сжатие, чтобы база данных росла медленнее. Проанализируем код записи:


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

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

Шаг 3. Открытие и запуск файла из Хранилища значения

Теперь разберем самую сложную часть — извлечение файла. Проблема автора темы заключалась в том, что система не могла определить программу для открытия, так как временный файл создавался без расширения. Выясним причину: метод ПолучитьИмяВременногоФайла() без параметров генерирует путь с расширением .tmp. Если мы просто приклеим к нему имя файла, Windows может запутаться. Правильный путь — передать расширение в сам метод генерации имени.

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


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

Теперь реализуем серверную функцию, которая извлечет данные из хранилища:


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

Оптимизация и безопасность

Работая с файлами, мы должны помнить об объеме базы данных. Рассмотрим несколько профессиональных советов:

  1. Использование томов хранения: Если файлов становится много (гигабайты), лучше перенастроить хранение на "Тома на диске". В этом случае в ХранилищеЗначения будет лежать не сам файл, а путь к нему. Это существенно ускорит создание резервных копий базы.
  2. Очистка временных файлов: Платформа 1С старается удалять временные файлы, созданные через ПолучитьИмяВременногоФайла, при закрытии сеанса. Однако при использовании ЗапуститьПриложение файл может быть заблокирован внешней программой (например, Excel), и удаление не произойдет. В высоконагруженных системах стоит предусмотреть регламентную очистку временных папок пользователей.
  3. Ограничение типов файлов: Для безопасности системы рекомендуется проверять расширения загружаемых файлов. Например, запрещать загрузку .exe, .bat или .vbs файлов, чтобы пользователи случайно не распространили вредоносное ПО через базу 1С.
  4. Размер файлов: При вызове метода Получить() у ХранилищаЗначения, весь файл целиком загружается в оперативную память сервера (в процесс rphost). Если 10 пользователей одновременно откроют видеофайл размером 500 Мб, сервер может исчерпать ресурсы памяти. Для работы с очень большими файлами лучше использовать механизмы БСП (Библиотеки стандартных подсистем).

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

← На главную