При разработке самописных конфигураций на платформе 1С:Предприятие 8.3 часто возникает задача организовать хранение различных файлов (документов Word, картинок, PDF) непосредственно в базе данных. Одной из наиболее распространенных проблем является ошибка получения навигационной ссылки при попытке открыть файл. Рассмотрим детально, как правильно реализовать этот механизм, разберем типичные ошибки и напишем надежный код для загрузки и выгрузки данных.
Часто разработчики пытаются использовать метод ПолучитьНавигационнуюСсылку для реквизита с типом ХранилищеЗначения. Однако стоит учитывать, что этот метод работает стабильно только тогда, когда реквизит напрямую привязан к форме или используется специфический формат пути. В случае с присоединенными файлами гораздо надежнее использовать работу с двоичными данными. Проанализируем ситуацию: когда мы помещаем файл в хранилище, платформа упаковывает его в специальный объект. Для его корректного извлечения на стороне клиента нам нужно сначала получить эти данные на сервере, передать их на клиент и записать в файл на диске.
Для начала разберем процесс выбора файла пользователем. В управляемых формах крайне важно использовать асинхронные методы, чтобы не блокировать работу интерфейса. Рассмотрим процедуру выбора файла:
&НаКлиенте
Процедура ЗагрузитьДокумент(Команда)
// Описываем оповещение, которое сработает после выбора файла
Оповещение = Новый ОписаниеОповещения("ОбработатьВыборФайла", ЭтотОбъект);
// Параметр Истина включает интерактивный выбор файла
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
После того как пользователь выбрал файл, управление передается в процедуру обработки. Здесь мы получаем адрес во временном хранилище, по которому файл будет доступен на сервере в течение текущего сеанса.
&НаКлиенте
Процедура ОбработатьВыборФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если Не Результат Тогда
Возврат; // Пользователь отменил выбор
КонецЕсли;
Модифицированность = Истина;
// Передаем данные на сервер для постоянного хранения
ЗаписатьВложениеНаСервере(Адрес, ВыбранноеИмяФайла);
КонецПроцедуры
На сервере нам необходимо создать элемент справочника и упаковать двоичные данные в ХранилищеЗначения. Рекомендуется использовать сжатие, чтобы база данных росла медленнее. Проанализируем код записи:
&НаСервере
Процедура ЗаписатьВложениеНаСервере(Адрес, ВыбранноеИмяФайла)
// Извлекаем расширение и имя через объект Файл
ФайлИнфо = Новый Файл(ВыбранноеИмяФайла);
НовыйФайл = Справочники.ПрисоединенныеФайлы.СоздатьЭлемент();
НовыйФайл.Наименование = ФайлИнфо.Имя;
НовыйФайл.ИмяБезРасширения = ФайлИнфо.ИмяБезРасширения;
НовыйФайл.Расширение = ФайлИнфо.Расширение; // Важно сохранить расширение отдельно!
// Получаем данные из временного хранилища
ДвоичныеДанные = ПолучитьИзВременногоХранилища(Адрес);
// Помещаем в реквизит типа ХранилищеЗначения со сжатием
НовыйФайл.ФайлХранилище = Новый ХранилищеЗначения(ДвоичныеДанные, Новый СжатиеДанных(9));
НовыйФайл.Записать();
КонецПроцедуры
Важный совет: всегда сохраняйте расширение файла в отдельном реквизите справочника. Это критически важно для последующего открытия файла операционной системой.
Теперь разберем самую сложную часть — извлечение файла. Проблема автора темы заключалась в том, что система не могла определить программу для открытия, так как временный файл создавался без расширения. Выясним причину: метод ПолучитьИмяВременногоФайла() без параметров генерирует путь с расширением .tmp. Если мы просто приклеим к нему имя файла, Windows может запутаться. Правильный путь — передать расширение в сам метод генерации имени.
Рассмотрим процедуру открытия файла при выборе в списке:
&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
СтандартнаяОбработка = Ложь;
ТекущиеДанные = Элементы.Список.ТекущиеДанные;
// Получаем структуру с данными файла с сервера
ДанныеФайла = ПолучитьДанныеФайлаНаСервере(ТекущиеДанные.Ссылка);
Если ДанныеФайла = Неопределено Тогда
Возврат;
КонецЕсли;
// Генерируем путь к временному файлу с правильным расширением
// Метод ПолучитьИмяВременногоФайла принимает расширение без точки
РасширениеОчищенное = СтрЗаменить(ДанныеФайла.Расширение, ".", "");
ИмяВременногоФайла = ПолучитьИмяВременногоФайла(РасширениеОчищенное);
// Записываем двоичные данные на диск клиента
ДанныеФайла.ДвоичныеДанные.Записать(ИмяВременногоФайла);
// Запускаем файл приложением по умолчанию
ЗапуститьПриложение(ИмяВременногоФайла);
КонецПроцедуры
Теперь реализуем серверную функцию, которая извлечет данные из хранилища:
&НаСервере
Функция ПолучитьДанныеФайлаНаСервере(СсылкаНаФайл)
ОбъектФайл = СсылкаНаФайл.ПолучитьОбъект();
// Извлекаем данные из хранилища
ДвоичныеДанные = ОбъектФайл.ФайлХранилище.Получить();
Если ТипЗнч(ДвоичныеДанные) <> Тип("ДвоичныеДанные") Тогда
Возврат Неопределено;
КонецЕсли;
Результат = Новый Структура;
Результат.Вставить("ДвоичныеДанные", ДвоичныеДанные);
Результат.Вставить("Расширение", ОбъектФайл.Расширение);
Возврат Результат;
КонецФункции
Работая с файлами, мы должны помнить об объеме базы данных. Рассмотрим несколько профессиональных советов:
ХранилищеЗначения будет лежать не сам файл, а путь к нему. Это существенно ускорит создание резервных копий базы.ПолучитьИмяВременногоФайла, при закрытии сеанса. Однако при использовании ЗапуститьПриложение файл может быть заблокирован внешней программой (например, Excel), и удаление не произойдет. В высоконагруженных системах стоит предусмотреть регламентную очистку временных папок пользователей..exe, .bat или .vbs файлов, чтобы пользователи случайно не распространили вредоносное ПО через базу 1С.Получить() у ХранилищаЗначения, весь файл целиком загружается в оперативную память сервера (в процесс rphost). Если 10 пользователей одновременно откроют видеофайл размером 500 Мб, сервер может исчерпать ресурсы памяти. Для работы с очень большими файлами лучше использовать механизмы БСП (Библиотеки стандартных подсистем).Таким образом, мы проанализировали полный цикл работы с файлами. Главный секрет успеха заключается в правильной передаче двоичных данных между клиентом и сервером и строгом контроле расширений файлов при их сохранении и последующем открытии.