Работа с внешними файлами — одна из самых частых задач при разработке в среде 1С:Предприятие 8.3. Часто возникает необходимость хранить в справочниках не только текстовую информацию, но и полноценные скан-копии документов в форматах PDF, PNG или JPG. В этой статье мы подробно разберем, как правильно организовать процесс загрузки файлов в базу, их безопасное хранение и последующее открытие пользователем.
Прежде чем переходить к написанию кода, проанализируем, где именно будут физически располагаться наши файлы. В системе 1С существует специальный тип данных — ХранилищеЗначения. Он позволяет упаковать любые двоичные данные (картинки, документы, архивы) непосредственно в реквизит справочника или документа.
Рассмотрим стандартную схему работы:
ХранилищеЗначения.Разберем процесс загрузки. Для этого нам понадобится форма справочника с реквизитом ДанныеФайла (тип ХранилищеЗначения) и строковый реквизит на форме АдресВоВременномХранилище для связи между клиентом и сервером.
Напишем процедуру для выбора файла. Обратите внимание, что мы используем асинхронный метод НачатьПомещениеФайла, что критически важно для работы в современных веб-клиентах:
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
// Описываем обработчик завершения выбора файла
Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
// Открываем диалог выбора файла
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если Не Результат Тогда
Возврат; // Пользователь отменил выбор
КонецЕсли;
// Сохраняем адрес временного хранилища в реквизит формы
АдресВоВременномХранилище = Адрес;
ЭтотОбъект.Модифицированность = Истина;
КонецПроцедуры
Теперь данные нужно сохранить в базу. Это лучше всего делать в серверном обработчике ПередЗаписьюНаСервере:
&НаСервере
Процедура ПередЗаписьюНаСервере(Отказ, ТекущийОбъект, ПараметрыЗаписи)
Если ЭтоАдресВременногоХранилища(АдресВоВременномХранилище) Тогда
// Извлекаем данные из хранилища и помещаем в реквизит объекта
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВоВременномХранилище);
ТекущийОбъект.ДанныеФайла = Новый ХранилищеЗначения(ДвоичныеДанные);
КонецЕсли;
КонецПроцедуры
Часто пользователю требуется выгрузить файл обратно на компьютер. Рассмотрим ситуацию, когда мы хотим сохранить файл в формате PDF. Для этого нам потребуется диалог сохранения файла. Проанализируем пример кода, который позволяет достать данные из базы и записать их по выбранному пути:
&НаКлиенте
Процедура ВыгрузитьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Сохранение);
Диалог.Фильтр = "Документ PDF (*.pdf)|*.pdf";
Диалог.Заголовок = "Укажите путь для сохранения файла";
Оповещение = Новый ОписаниеОповещения("ПослеВыбораПутиДляСохранения", ЭтотОбъект);
Диалог.Показать(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораПутиДляСохранения(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Если ВыбранныеФайлы <> Неопределено Тогда
// Получаем двоичные данные с сервера
ДвоичныеДанные = ПолучитьДанныеФайлаССервера(Объект.Ссылка);
Если ДвоичныеДанные <> Неопределено Тогда
ДвоичныеДанные.Записать(ВыбранныеФайлы[0]);
Сообщить("Файл успешно сохранен: " + ВыбранныеФайлы[0]);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
На стороне сервера функция извлечения данных будет выглядеть следующим образом:
&НаСервереБезКонтекста
Функция ПолучитьДанныеФайлаССервера(Ссылка)
ОбъектФайла = Ссылка.ПолучитьОбъект();
Если ОбъектФайла.ДанныеФайла = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
// Извлекаем содержимое из ХранилищаЗначения
Возврат ОбъектФайла.ДанныеФайла.Получить();
КонецФункции
Самый удобный для пользователя вариант — нажать кнопку "Открыть" и увидеть содержимое PDF во внешней программе (например, в Adobe Reader или браузере), не выбирая каждый раз путь сохранения. Выясним причину, почему это иногда вызывает сложности. Дело в том, что 1С не может запустить файл напрямую из оперативной памяти — его в любом случае нужно временно "приземлить" на жесткий диск.
Рассмотрим алгоритм "бесшовного" открытия:
&НаКлиенте
Процедура ОткрытьФайл(Команда)
// 1. Получаем данные (в реальном коде лучше использовать асинхронность)
ДвоичныеДанные = ПолучитьДанныеФайлаССервера(Объект.Ссылка);
Если ДвоичныеДанные = Неопределено Тогда
Возврат;
КонецЕсли;
// 2. Генерируем уникальное имя во временном каталоге
ИмяВременногоФайла = ПолучитьИмяВременногоХранилища() + ".pdf";
// Или используем стандартный метод:
ПутьКФайлу = ПолучитьИмяВременногоФайла(".pdf");
// 3. Записываем данные в этот файл
ДвоичныеДанные.Записать(ПутьКФайлу);
// 4. Запускаем приложение, ассоциированное с PDF
#Если МобильноеПриложениеКлиент Тогда
// Особенности мобильной платформы
#Иначе
ЗапуститьПриложение(ПутьКФайлу);
#КонецЕсли
КонецПроцедуры
В современных конфигурациях 1С (начиная с версии 8.3.15+) использование метода ЗапуститьПриложение может вызвать ошибку: "Использование синхронных методов на клиенте запрещено". Это связано с политикой безопасности браузеров и производительностью интерфейса.
Чтобы избежать этой ошибки, у нас есть два пути:
Режим использования синхронных вызовов... в значение "Использовать". Это решит проблему быстро, но может привести к зависаниям интерфейса.НачатьЗапускПриложения().Проанализируем правильный подход:
&НаКлиенте
Процедура ОткрытьФайлАсинхронно(Команда)
// ... получение данных и запись во временный файл (см. выше) ...
ОписаниеОповещения = Новый ОписаниеОповещения("ПослеЗапускаПриложения", ЭтотОбъект);
НачатьЗапускПриложения(ОписаниеОповещения, ПутьКФайлу);
КонецПроцедуры
&НаКлиенте
Процедура ПослеЗапускаПриложения(КодВозврата, ДополнительныеПараметры) Экспорт
// Здесь можно выполнить дополнительные действия после открытия
КонецПроцедуры
При работе с файлами в 1С стоит учитывать несколько критически важных моментов, которые помогут сохранить производительность системы:
1. Объемы базы данных. Хранение PDF-файлов непосредственно в SQL-базе приводит к ее взрывному росту. Рассмотрим альтернативу: Тома хранения файлов. В этом случае 1С хранит в базе только ссылку, а сам файл лежит в сетевой папке на сервере. Это значительно ускоряет создание бэкапов.
2. Очистка временных файлов. Файлы, созданные через ПолучитьИмяВременногоФайла, не всегда удаляются мгновенно. Если ваши пользователи открывают сотни документов в день, папка Temp может переполниться. Хорошей практикой считается удаление временного файла после его закрытия, хотя программно отследить момент закрытия внешнего приложения (например, Adobe Reader) бывает сложно.
3. Расширения файлов. Не стоит жестко прописывать расширение .pdf в коде. Рекомендуем создать в справочнике отдельное поле РасширениеФайла. При загрузке сохраняйте его (например, "pdf", "docx", "jpg"), а при открытии формируйте имя временного файла динамически: Путь = ПолучитьИмяВременногоФайла("." + Объект.РасширениеФайла);. Это сделает ваш механизм универсальным для любых типов документов.
4. Использование БСП. Если вы работаете в типовой конфигурации (БП 3.0, УТ 11, ERP), разберите по шагам подсистему "Работа с файлами" из состава Библиотеки стандартных подсистем (БСП). В ней уже реализованы все вышеописанные механизмы, включая версионность файлов, сканирование прямо из формы 1С и антивирусную проверку.
Таким образом, мы рассмотрели полный цикл работы с PDF-файлами: от выбора на клиенте до открытия во внешнем приложении. Используя асинхронные методы и временные хранилища, вы обеспечите стабильную работу вашей системы как в тонком клиенте, так и через веб-интерфейс.