Загрузка файлов в базу 1С из кода: форма, HTTP-сервис, COM и обменная папка
Содержание
Чтобы программно добавить файл
в 1С 8.3, его двоичные данные сначала доставляют на сервер (через временное хранилище, тело HTTP-запроса или параметр внешнего соединения), а затем записывают в реквизит типа ХранилищеЗначения или в справочник присоединённых файлов БСП. Источник файла может быть любым: диалог выбора у пользователя, внешняя система, скрипт на другом компьютере или общая сетевая папка.
Сложность обычно не в самой записи, а в доставке. Клиент и сервер 1С работают на разных машинах, и путь C:\Сканы\акт.pdf с компьютера бухгалтера сервер просто не увидит. Ниже четыре рабочих способа под разные сценарии и код, который можно взять за основу.
Как платформа 1С работает с файлами: клиент, сервер и двоичные данные
В клиент-серверном варианте код с директивой &НаКлиенте видит диск пользователя, а код &НаСервере — диск сервера 1С. Конструктор Новый ДвоичныеДанные(Путь) читает файл там, где он выполняется. Это первое, о чём спотыкаются: на сервере такой вызов ищет файл на сервере.
Основные объекты, которые понадобятся:
- 📦
ДвоичныеДанные— содержимое файла в памяти. Умеет записываться на диск методомЗаписать()и считать размер черезРазмер(). - 🗄️
ХранилищеЗначения— тип реквизита для хранения данных в базе. Принимает второй параметрНовый СжатиеДанных(9)для сжатия. - ⏳ Временное хранилище — механизм передачи данных между клиентом и сервером:
ПоместитьВоВременноеХранилище()иПолучитьИзВременногоХранилища(). - 📁
Файл— описание файла по пути: свойстваИмя,ИмяБезРасширения,Расширение(с точкой), методыСуществует()иЭтоКаталог(). - 🔤
Base64Строка()иBase64Значение()— превращают двоичные данные в строку и обратно. Нужны там, где канал передаёт только текст.
| Способ | Когда подходит | Ограничения |
|---|---|---|
| Форма и временное хранилище | Пользователь сам выбирает файл | Нужен открытый клиент 1С |
| HTTP-сервис | Файлы шлёт сайт, скрипт или другая база | Нужна публикация на веб-сервере |
| COM-соединение | Скрипт Windows на машине с установленной платформой | Только Windows, совпадение разрядности |
| Обменная папка | Сканер или внешняя программа кладёт файлы в каталог | Права службы сервера 1С на папку |
Добавление файла из формы через временное хранилище
Классическая схема для кнопки «Загрузить файл» на форме. Клиент показывает диалог, платформа передаёт файл на сервер во временное хранилище, а серверная процедура забирает его оттуда и записывает в базу.
Пример ниже работает с собственным справочником ФайлыКонтрагентов. У него реквизиты Владелец (ссылка на контрагента), ИмяФайла (строка), Расширение (строка) и Данные (тип ХранилищеЗначения).
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайла(Оповещение, , , Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если Не Результат Тогда
Возврат; // пользователь закрыл диалог
КонецЕсли;
СохранитьФайлНаСервере(Адрес, ВыбранноеИмяФайла);
Элементы.СписокФайлов.Обновить();
КонецПроцедуры
&НаСервере
Процедура СохранитьФайлНаСервере(Адрес, ПолноеИмя)
Данные = ПолучитьИзВременногоХранилища(Адрес);
ОписаниеФайла = Новый Файл(ПолноеИмя);
Элемент = Справочники.ФайлыКонтрагентов.СоздатьЭлемент();
Элемент.Владелец = Объект.Ссылка;
Элемент.Наименование = ОписаниеФайла.ИмяБезРасширения;
Элемент.ИмяФайла = ОписаниеФайла.Имя;
Элемент.Расширение = ОписаниеФайла.Расширение;
Элемент.Данные = Новый ХранилищеЗначения(Данные, Новый СжатиеДанных(9));
Элемент.Записать();
УдалитьИзВременногоХранилища(Адрес);
КонецПроцедуры
Три детали, которые часто упускают:
- Передавайте
УникальныйИдентификаторформы последним параметром. Тогда временное хранилище живёт, пока открыта форма, и данные не пропадут между вызовами. - У нового объекта формы
Объект.Ссылкапустая. Сначала запишите карточку контрагента, потом прикрепляйте файлы. - В платформе 8.3.18 и новее появились асинхронные методы
ПоместитьФайлНаСерверАсинх()иПоместитьФайлыНаСерверАсинх(). Схема та же: на сервер приходит адрес во временном хранилище.
Прочитать файл обратно и отдать пользователю так же просто: на сервере Элемент.Данные.Получить() возвращает ДвоичныеДанные, их кладут во временное хранилище, а на клиенте вызывают ПолучитьФайл() или НачатьПолучениеФайлов().
Если в конфигурации есть БСП
В типовых конфигурациях (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) файлы хранятся через подсистему «Работа с файлами» Библиотеки стандартных подсистем. Свой справочник там заводить не нужно: у объектов уже есть справочники присоединённых файлов, например ДоговорыКонтрагентовПрисоединенныеФайлы.
В БСП 3.x файл добавляют серверной функцией РаботаСФайлами.ДобавитьФайл(). Параметры готовят через РаботаСФайлами.ПараметрыДобавленияФайла():
&НаСервере
Функция ПрикрепитьФайлБСП(Владелец, Адрес, ПолноеИмя)
ОписаниеФайла = Новый Файл(ПолноеИмя);
ПараметрыФайла = РаботаСФайлами.ПараметрыДобавленияФайла();
ПараметрыФайла.ВладелецФайлов = Владелец;
ПараметрыФайла.ИмяБезРасширения = ОписаниеФайла.ИмяБезРасширения;
ПараметрыФайла.РасширениеБезТочки = Сред(ОписаниеФайла.Расширение, 2);
Возврат РаботаСФайлами.ДобавитьФайл(ПараметрыФайла, Адрес);
КонецФункции
Плюс БСП в том, что она сама решает, где лежат данные: в базе или в томах на диске. Режим хранения переключается в разделе Администрирование, в настройках работы с файлами. Ваш код от этого не меняется.
Состав полей структуры параметров может различаться между версиями библиотеки. Перед вызовом откройте общий модуль РаботаСФайлами в конфигураторе и сверьте комментарий к функции ПараметрыДобавленияФайла().
Приём файла в 1С через HTTP-сервис
HTTP-сервис нужен, когда файл приходит извне: с сайта, из мобильного приложения, из другой базы или от скрипта на Python. Внешняя сторона отправляет POST-запрос с содержимым файла в теле, 1С сохраняет его и отвечает кодом.
Создание сервиса в конфигураторе
- В дереве конфигурации откройте
Общие → HTTP-сервисыи добавьте сервис, напримерFilesс корневым URLfiles. - Добавьте шаблон URL
/upload/{name}и в нём методPOST. - В свойствах метода создайте обработчик. Конфигуратор сгенерирует функцию в модуле сервиса.
- Опубликуйте базу на веб-сервере (IIS или Apache) с флажком публикации HTTP-сервисов.
- Заведите пользователя-робота с ролью, у которой есть права на запись нужных объектов.
Обработчик метода:
Функция UploadPOST(Запрос)
ИмяФайла = Запрос.ПараметрыURL.Получить("name");
Данные = Запрос.ПолучитьТелоКакДвоичныеДанные();
Если Данные = Неопределено Или Данные.Размер() = 0 Тогда
Ответ = Новый HTTPСервисОтвет(400);
Ответ.УстановитьТелоИзСтроки("Пустое тело запроса");
Возврат Ответ;
КонецЕсли;
Попытка
Ссылка = ЗагрузкаФайловСервер.СохранитьВложение(ИмяФайла, Данные);
Ответ = Новый HTTPСервисОтвет(201);
Ответ.УстановитьТелоИзСтроки(Строка(Ссылка.УникальныйИдентификатор()));
Исключение
ЗаписьЖурналаРегистрации("ЗагрузкаФайлов.HTTP", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Ответ = Новый HTTPСервисОтвет(500);
КонецПопытки;
Возврат Ответ;
КонецФункции
ЗагрузкаФайловСервер здесь — ваш общий серверный модуль, где лежит запись в справочник (как в процедуре из формы выше). Так один и тот же код сохранения обслуживает все каналы.
Проверить сервис можно утилитой curl с любой машины:
curl -X POST -u Robot:Пароль --data-binary @C:\in\scan.pdf ^
-H "Content-Type: application/octet-stream" ^
http://srv-1c/buh/hs/files/upload/scan.pdf
Если файл отправляет другая база 1С, используйте HTTPСоединение. Метод УстановитьИмяФайлаТела() читает тело прямо с диска и не держит весь файл в памяти:
Соединение = Новый HTTPСоединение("srv-1c", 80, "Robot", "Пароль", , 60);
Запрос = Новый HTTPЗапрос("/buh/hs/files/upload/scan.pdf");
Запрос.Заголовки.Вставить("Content-Type", "application/octet-stream");
Запрос.УстановитьИмяФайлаТела("C:\in\scan.pdf");
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
Если Ответ.КодСостояния <> 201 Тогда
ВызватьИсключение СтрШаблон("Сервис вернул код %1", Ответ.КодСостояния);
КонецЕсли;
IIS по умолчанию ограничивает размер тела запроса примерно 30 МБ (параметр maxAllowedContentLength). Если сканы или архивы крупнее, увеличьте лимит в web.config публикации, иначе запрос отклонит веб-сервер ещё до 1С.
Кириллицу в имени файла внутри URL лучше не передавать: она приходит в URL-кодировке. Либо раскодируйте строку через РаскодироватьСтроку(Имя, СпособКодированияСтроки.КодировкаURL), либо передавайте имя отдельным заголовком.
Загрузка файла через COM-соединение
COM-соединение подходит для скриптов Windows: планировщик заданий запускает PowerShell, тот подключается к базе и передаёт файл. Работает только на машине, где установлена платформа 1С и зарегистрирована библиотека comcntr.dll.
В базе создайте общий модуль с флажками Сервер и Внешнее соединение и экспортной функцией. Файл удобнее передавать строкой Base64: так не нужно, чтобы сервер видел диск скрипта.
Функция ПринятьФайл(ИмяФайла, ДанныеBase64) Экспорт
Данные = Base64Значение(ДанныеBase64);
Ссылка = ЗагрузкаФайловСервер.СохранитьВложение(ИмяФайла, Данные);
Возврат Строка(Ссылка.УникальныйИдентификатор());
КонецФункции
Скрипт PowerShell (модуль назван ЗагрузкаФайловCOM):
$connector = New-Object -ComObject V83.COMConnector
$base = $connector.Connect('Srvr="srv-1c";Ref="buh";Usr="Robot";Pwd="Пароль"')
$bytes = [IO.File]::ReadAllBytes('C:\in\scan.pdf')
$b64 = [Convert]::ToBase64String($bytes)
$id = $base.ЗагрузкаФайловCOM.ПринятьФайл('scan.pdf', $b64)
Write-Output "Загружено: $id"
Для файловой базы строка подключения выглядит так: File="D:\Bases\Buh";Usr="Robot";Pwd="Пароль".
Типичные проблемы COM:
- 🧩
Недопустимая строка с указанием класса— компонента не зарегистрирована. Выполните от администратораregsvr32 "C:\Program Files\1cv8\<версия>\bin\comcntr.dll". - 🔀 Разрядность процесса не совпадает с разрядностью платформы. 64-битный PowerShell не создаст объект 32-битной платформы, и наоборот.
- 🔄 Версия
comcntr.dllдолжна совпадать с версией сервера 1С. После обновления платформы регистрацию повторяют. - 🐘 Base64 увеличивает объём данных примерно на треть. Для файлов в сотни мегабайт COM не лучший канал, берите HTTP-сервис или папку.
Обмен файлами через сетевую папку
Самый простой для внешних систем вариант: сканер или чужая программа кладёт файлы в каталог, а регламентное задание 1С раз в несколько минут забирает их в базу. Никаких веб-серверов и COM.
Процедура ЗагрузитьИзОбменнойПапки() Экспорт
Каталог = "\\fs01\obmen\vhod\";
Файлы = НайтиФайлы(Каталог, "*.pdf", Ложь);
Для Каждого ФайлВПапке Из Файлы Цикл
Если ФайлВПапке.ЭтоКаталог() Тогда
Продолжить;
КонецЕсли;
Попытка
Данные = Новый ДвоичныеДанные(ФайлВПапке.ПолноеИмя);
ЗагрузкаФайловСервер.СохранитьВложение(ФайлВПапке.Имя, Данные);
ПереместитьФайл(ФайлВПапке.ПолноеИмя, Каталог + "done\" + ФайлВПапке.Имя);
Исключение
ЗаписьЖурналаРегистрации("ЗагрузкаФайлов.Папка", УровеньЖурналаРегистрации.Ошибка, , ,
ФайлВПапке.ПолноеИмя + Символы.ПС + ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ПереместитьФайл(ФайлВПапке.ПолноеИмя, Каталог + "error\" + ФайлВПапке.Имя);
КонецПопытки;
КонецЦикла;
КонецПроцедуры
Процедуру назначьте методом регламентного задания в Общие → Регламентные задания и задайте расписание. Подкаталоги done и error создайте заранее.
На что смотреть в этом сценарии:
- Регламентное задание выполняется на сервере 1С от имени учётной записи службы агента сервера, по умолчанию
USR1CV8. Именно ей нужны права на чтение и запись в папку. Права пользователя, который настраивал задание, роли не играют. - Сетевые диски вроде
Z:\служба не видит. Указывайте UNC-путь\\сервер\папка. - Внешняя программа может ещё дописывать файл, когда задание его заберёт. Договоритесь, что запись идёт с временным расширением
.tmp, а после завершения файл переименовывают. Маска*.pdfтогда пропустит недописанное. - Перемещайте обработанные файлы, а не удаляйте сразу. Папка
doneслужит страховкой на случай ошибки в коде записи.
Где хранить вложения: в базе или на диске
Хранение в ХранилищеЗначения надёжно: файлы попадают в резервную копию вместе с базой, их не потеряешь при переносе. Цена — рост размера базы и выгрузки .dt.
| Вариант | Плюсы | Минусы |
|---|---|---|
Реквизит ХранилищеЗначения в объекте | Один объект, просто читать | Тяжёлый объект грузится целиком при открытии формы |
| Отдельный справочник с хранилищем | Документ открывается быстро, файлы грузятся по запросу | Нужна своя форма списка |
| Тома на диске (БСП) | База не растёт, тяжёлые архивы не мешают | Папки томов нужно бэкапить отдельно |
Не кладите хранилище с файлом прямо в шапку часто открываемого документа. Каждый раз при открытии формы платформа тащит с сервера все мегабайты. Отдельный справочник со ссылкой на владельца решает это.
Новый СжатиеДанных(9) даёт максимальное сжатие. Для PDF и JPEG выигрыш небольшой, они уже сжаты. Для XML, текстов и выгрузок Excel старого формата разница бывает в разы.
Обработка ошибок и журнал загрузки
Любая загрузка файлов должна переживать сбой одного файла и не останавливать остальные. Поэтому каждый файл обрабатывайте в своей Попытка ... Исключение, как в примерах выше.
Что писать в журнал регистрации:
- 🏷️ Имя события с префиксом подсистемы, например
ЗагрузкаФайлов.HTTP. По нему потом фильтруют журнал. - 📝 Имя или путь файла и
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()). Краткое представление теряет стек вызовов. - ✅ Успешные загрузки — с уровнем
УровеньЖурналаРегистрации.Информацияи ссылкой на созданный объект в параметре данных.
Журнал открывают в пользовательском режиме через Администрирование → Обслуживание → Журнал регистрации (в Бухгалтерии 3.0) или в конфигураторе через Администрирование → Журнал регистрации. Отбор по имени события сразу показывает все сбои загрузки.
Перед записью стоит проверить входные данные:
- Размер. Ограничьте максимум, например 20 МБ, и отклоняйте больше:
Если Данные.Размер() > 20 * 1024 * 1024 Тогда. - Расширение. Разрешите список (
.pdf,.jpg,.png,.xml) и отбрасывайте исполняемые файлы. - Дубли. Если внешняя система может прислать файл дважды, храните хеш. Объект
ХешированиеДанныхс типомХешФункция.SHA256считает его по двоичным данным, а поиск по реквизиту с хешем отсекает повторы.
/<имя публикации>/hs/<корневой URL>/<шаблон>.Частые вопросы
Можно ли записать файл в базу 1С без формы и пользователя?
Как программно выгрузить файл из базы обратно на диск?
ДвоичныеДанные методом Получить() у хранилища и вызовите Данные.Записать("\\fs01\out\имя.pdf"). Путь должен быть доступен серверу 1С.Почему после загрузки файлов база сильно выросла?
ХранилищеЗначения хранится внутри базы. Включите сжатие, вынесите файлы в отдельный справочник или переведите хранение БСП в тома на диске.Работает ли загрузка через форму в веб-клиенте?
НачатьПомещениеФайла() и асинхронные методы помещения работают и в тонком, и в веб-клиенте. Синхронный ПоместитьФайл() в веб-клиенте использовать нельзя.Как передать несколько файлов одним действием?
НачатьПомещениеФайлов() или ПоместитьФайлыНаСерверАсинх(). Во внешней интеграции проще отправлять файлы отдельными запросами: сбой одного не сорвёт остальные.