Загрузка файлов в базу 1С из кода: диалог, HTTP, COM и общая папка

· 10 мин

Содержание

Чтобы добавить файл в 1С программно, его сначала превращают в ДвоичныеДанные на сервере, а потом записывают в реквизит типа ХранилищеЗначения или в справочник файлов БСП. С клиента файл попадает на сервер через временное хранилище (НачатьПомещениеФайла или ПоместитьФайлНаСерверАсинх), из внешних систем — через HTTP-сервис, COM-соединение или общую папку.

Ниже код для каждого канала на платформе 8.3 и приёмы, на которых чаще всего спотыкаются: пути на сервере, права службы, лимиты веб-сервера и запись ошибок в журнал регистрации.

Где живёт файл: клиент, сервер и временное хранилище

Главное правило программной работы с файлами в 1С: клиент и сервер — разные машины. Путь C:\Users\buh\Desktop\act.pdf существует только на компьютере пользователя. Серверная процедура его не увидит, даже если база файловая и всё крутится на одном ПК, а вы потом переедете на клиент-сервер.

Как данные переходят между сторонами:

  • 📦 ДвоичныеДанные — содержимое файла в памяти. Создаётся конструктором Новый ДвоичныеДанные(ПолноеИмяФайла) там, где файл физически лежит.
  • 🔄 Временное хранилище — мост между клиентом и сервером. ПоместитьВоВременноеХранилище() возвращает строку-адрес, ПолучитьИзВременногоХранилища(Адрес) возвращает данные.
  • 💾 ХранилищеЗначения — тип реквизита, в котором файл хранится в базе постоянно.
  • 📁 Объект Файл — разбирает путь (Имя, Расширение, ПолноеИмя) и проверяет наличие файла методом Существует().

Если временное хранилище привязано к уникальному идентификатору формы, данные живут, пока открыта форма. Без идентификатора — только до конца текущего серверного вызова. Поэтому из диалога файл помещают с параметром УникальныйИдентификатор.

Какой канал загрузки выбрать:

КаналКогда подходитНа что смотреть
Диалог выбора файлаПользователь сам прикладывает скан, договор, выпискуРаботает и в веб-клиенте
HTTP-сервисСайт, мобильное приложение, другая учётная системаПубликация на веб-сервере, лимит размера тела
COM-соединениеСкрипт или программа под Windows на той же сетиРазрядность, регистрация comcntr.dll
Общая папкаСканер, ЭДО-шлюз, выгрузка из чужой системы файламиПрава служебного пользователя сервера 1С

Как добавить файл из диалога выбора программно

Классическая задача: кнопка «Прикрепить» на форме документа. Файл выбирается на клиенте, уходит во временное хранилище, а запись в базу выполняет сервер.

Вариант с описанием оповещения

Работает без модальных окон, поэтому подходит для конфигураций с режимом «Не использовать» модальности:

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

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

&НаСервере
Процедура СохранитьНаСервере(Адрес, ИмяФайла)
	Данные = ПолучитьИзВременногоХранилища(Адрес);
	ЗагрузкаФайлов.СохранитьФайл(ИмяФайла, Данные);
	УдалитьИзВременногоХранилища(Адрес);
КонецПроцедуры

Имя файла вычисляйте на клиенте. Если сервер 1С работает под Linux, объект Файл на сервере не разберёт путь с обратными слешами, и в Имя попадёт строка целиком.

Вариант с Асинх и Ждать

С платформы 8.3.18 тот же сценарий короче:

&НаКлиенте
Асинх Процедура ПрикрепитьФайл(Команда)
	Описание = Ждать ПоместитьФайлНаСерверАсинх(, , , УникальныйИдентификатор);
	Если Описание = Неопределено Или Описание.ПомещениеФайлаОтменено Тогда
		Возврат;
	КонецЕсли;
	СохранитьНаСервере(Описание.Адрес, Описание.СсылкаНаФайл.Имя);
КонецПроцедуры

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

Общий модуль для записи

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

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

Сжатие уровня 9 заметно уменьшает XML и текстовые файлы. PDF, JPEG и архивы уже сжаты, на них выигрыш близок к нулю.

Загрузка файла в 1С через HTTP-сервис

HTTP-сервис — самый удобный способ принять файл извне: отправитель не ставит ничего, кроме обычного HTTP-клиента. Сервис создаётся в конфигураторе в ветке Общие → HTTP-сервисы, база публикуется на веб-сервере (IIS или Apache) с флагом публикации HTTP-сервисов.

Настройка сервиса

  1. Создайте HTTP-сервис, корневой URL — например, files.
  2. Добавьте шаблон URL /upload/{name}.
  3. В шаблоне создайте метод POST и обработчик ЗагрузитьPOST.
  4. Опубликуйте базу. Адрес сервиса будет вида https://srv.example.ru/buh/hs/files/upload/act.pdf, где buh — имя публикации.

Обработчик на стороне 1С

Файл передаётся «сырым» телом запроса, без multipart. Платформа не разбирает multipart/form-data сама, а с бинарным телом всё делается одной строкой:

Функция ЗагрузитьPOST(Запрос)
	ИмяФайла = Запрос.ПараметрыURL.Получить("name");
	Данные = Запрос.ПолучитьТелоКакДвоичныеДанные();

	Если Данные = Неопределено Или Данные.Размер() = 0 Тогда
		Ответ = Новый HTTPСервисОтвет(400);
		Ответ.УстановитьТелоИзСтроки("Пустое тело запроса");
		Возврат Ответ;
	КонецЕсли;

	Попытка
		Ссылка = ЗагрузкаФайлов.СохранитьФайл(ИмяФайла, Данные);
	Исключение
		ЗаписьЖурналаРегистрации("ЗагрузкаФайлов.HTTP", УровеньЖурналаРегистрации.Ошибка, , ИмяФайла,
			ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
		Возврат Новый HTTPСервисОтвет(500);
	КонецПопытки;

	Ответ = Новый HTTPСервисОтвет(201);
	Ответ.УстановитьТелоИзСтроки(Строка(Ссылка.УникальныйИдентификатор()));
	Возврат Ответ;
КонецФункции

Проверить сервис можно из командной строки:

curl -u exchange:Пароль --data-binary @act.pdf -H "Content-Type: application/octet-stream" https://srv.example.ru/buh/hs/files/upload/act.pdf

Отправка файла из другой базы 1С

Если отправитель — тоже 1С, используйте HTTPСоединение и УстановитьИмяФайлаТела. Файл читается с диска потоком, без загрузки в память целиком:

Соединение = Новый HTTPСоединение("srv.example.ru", 443, "exchange", Пароль, , 60,
	Новый ЗащищенноеСоединениеOpenSSL);
Адрес = "/buh/hs/files/upload/" + КодироватьСтроку(ИмяФайла, СпособКодированияСтроки.КодировкаURL);
Запрос = Новый HTTPЗапрос(Адрес);
Запрос.Заголовки.Вставить("Content-Type", "application/octet-stream");
Запрос.УстановитьИмяФайлаТела(ПолноеИмяФайла);
Ответ = Соединение.ВызватьHTTPМетод("POST", Запрос);
Если Ответ.КодСостояния <> 201 Тогда
	ВызватьИсключение "Сервер вернул код " + Формат(Ответ.КодСостояния, "ЧГ=0");
КонецЕсли;

IIS по умолчанию режет тело запроса больше 30 000 000 байт (примерно 28,6 МБ): параметр maxAllowedContentLength в фильтрации запросов. В Apache размер ограничивает директива LimitRequestBody. Отказ происходит до 1С, поэтому в журнале регистрации вы его не увидите — только код 404.13 или 413 у отправителя.

Добавление файла через COM-соединение

COM-соединение подходит, когда файл приходит в программу или скрипт под Windows: робот разбора почты, утилита сканера, скрипт на Python или PowerShell. Внешнее приложение подключается к базе и вызывает экспортную функцию общего модуля.

Подготовка

  • ⚙️ Зарегистрируйте компоненту от имени администратора: regsvr32 "C:\Program Files\1cv8\8.3.xx.xxxx\bin\comcntr.dll" (подставьте свою версию платформы).
  • 🔢 Разрядность платформы и вызывающего процесса должна совпадать. 64-битный Python не создаст объект из 32-битной платформы.
  • ☑️ У общего модуля включите флаги «Сервер» и «Внешнее соединение», иначе его функции не будут видны через COM.

Почему передавать Base64, а не путь

Передать путь к файлу заманчиво, но опасно. В клиент-серверной базе серверный код выполняется на сервере 1С, и путь D:\in\act.pdf машины со скриптом там не существует. Надёжнее прочитать файл во внешней программе, перевести в Base64 и отдать строкой:

// Общий модуль ЗагрузкаФайлов, флаги «Сервер» и «Внешнее соединение»
Функция ДобавитьИзBase64(ИмяФайла, СтрокаBase64) Экспорт
	Данные = Base64Значение(СтрокаBase64);
	Ссылка = СохранитьФайл(ИмяФайла, Данные);
	Возврат Строка(Ссылка.УникальныйИдентификатор());
КонецФункции

Вызов из Python (пакет pywin32):

import base64
import win32com.client

connector = win32com.client.Dispatch("V83.COMConnector")
conn = connector.Connect('Srvr="srv1c";Ref="buh";Usr="exchange";Pwd="secret";')

with open(r"D:\in\act.pdf", "rb") as f:
    data = base64.b64encode(f.read()).decode("ascii")

uid = conn.ЗагрузкаФайлов.ДобавитьИзBase64("act.pdf", data)
print(uid)

Для файловой базы строка подключения выглядит как File="D:\Bases\buh";Usr="exchange";Pwd="secret";.

Base64 раздувает данные примерно на треть. Для файлов по 100 МБ и больше COM неудобен, лучше HTTP-сервис или общая папка.

Каждое COM-соединение — полноценный сеанс и занимает клиентскую лицензию. Не открывайте соединение на каждый файл: подключитесь один раз и загрузите пачку.

Программная загрузка файлов из общей папки

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

Права и пути

Регламентное задание выполняется на сервере 1С от имени служебной учётной записи (при установке по умолчанию USR1CV8). Значит:

  1. Путь указывайте в формате UNC: \\fs01\obmen\in\. Подключённые диски вроде Z: есть только в сеансе пользователя Windows, служба их не видит.
  2. Дайте служебной учётной записи права на чтение, запись и удаление в папке и подпапках.
  3. Заранее создайте подпапки work, done и error.

Код регламентного задания

Файл сначала переносится в work, а уже потом читается. Так недописанный файл не попадёт в базу дважды: если перенос не удался, файл ещё занят, и задание вернётся к нему в следующий запуск.

Процедура ЗагрузитьИзПапки() Экспорт
	Каталог = "\\fs01\obmen\in\";
	Файлы = НайтиФайлы(Каталог, "*.pdf", Ложь);

	Для Каждого Файл Из Файлы Цикл
		ВРаботе = Каталог + "work\" + Файл.Имя;
		Попытка
			ПереместитьФайл(Файл.ПолноеИмя, ВРаботе);
		Исключение
			Продолжить; // файл ещё пишется или занят
		КонецПопытки;

		Попытка
			Данные = Новый ДвоичныеДанные(ВРаботе);
			ЗагрузкаФайлов.СохранитьФайл(Файл.Имя, Данные);
			ПереместитьФайл(ВРаботе, Каталог + "done\" + Файл.Имя);
		Исключение
			ЗаписьЖурналаРегистрации("ЗагрузкаФайлов.Папка", УровеньЖурналаРегистрации.Ошибка, , Файл.Имя,
				ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
			ПереместитьФайл(ВРаботе, Каталог + "error\" + Файл.Имя);
		КонецПопытки;
	КонецЦикла;
КонецПроцедуры

Если сервер 1С под Linux, папку монтируют в файловую систему сервера (например, через CIFS), а в коде указывают локальный путь точки монтирования, /mnt/obmen/in/.

Если в папку регулярно приходят файлы с одинаковыми именами (scan.pdf, export.xml), добавляйте к имени в done и error дату и время через Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMddHHmmss"). Иначе новый файл столкнётся со старым, и разбираться, какой из них уже в базе, придётся вручную.

Как хранить добавленный файл в базе 1С

После загрузки файл где-то должен жить. Вариантов три, и выбор влияет на размер базы и скорость резервного копирования.

СпособПлюсыМинусы
Реквизит ХранилищеЗначения в своём объектеПросто, файл едет вместе с выгрузкой .dtБаза растёт, бэкап тяжелеет
Присоединённые файлы БСПВерсии, права, электронная подпись, просмотр из карточкиНужно разобраться в API подсистемы
Тома на диске (настройка БСП)База не пухнет от скановТома надо копировать отдельно от базы

Файл в собственном реквизите

Пример выше уже пишет файл в ХранилищеЗначения. Обратно данные получают методом Получить():

Данные = Ссылка.Данные.Получить(); // ДвоичныеДанные
ИмяВременного = ПолучитьИмяВременногоФайла("pdf");
Данные.Записать(ИмяВременного);

На форму реквизит ХранилищеЗначения не выводится и на клиент не передаётся. Чтобы отдать файл пользователю, положите данные во временное хранилище и вызовите на клиенте получение файла по адресу.

Присоединённые файлы в типовых конфигурациях

В конфигурациях на БСП 3.x (Бухгалтерия 3.0, ЗУП 3.1, УТ 11) правильнее не изобретать свой справочник, а прикрепить файл к объекту штатно. Для этого в общем модуле РаботаСФайлами есть функция ДобавитьФайл и конструктор параметров ПараметрыДобавленияФайла():

Параметры = РаботаСФайлами.ПараметрыДобавленияФайла();
Параметры.ВладелецФайлов = ДокументСсылка;
Параметры.ИмяБезРасширения = "Акт сверки";
Параметры.РасширениеБезТочки = "pdf";
Адрес = ПоместитьВоВременноеХранилище(Данные);
ФайлСсылка = РаботаСФайлами.ДобавитьФайл(Параметры, Адрес);

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

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

Ошибки при загрузке и журнал регистрации

Программная загрузка обычно идёт без пользователя, поэтому ошибку никто не увидит, пока её не запишут. Правило одно: каждый канал оборачивает запись в Попытка…Исключение и пишет подробности в журнал регистрации.

Как писать в журнал

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

Имя события делайте иерархическим через точку: ЗагрузкаФайлов.HTTP, ЗагрузкаФайлов.Папка. Тогда в журнале регистрации (Администрирование → Обслуживание → Журнал регистрации в типовых) фильтр по событию соберёт все сбои канала. На платформе 8.3.17 и новее вместо глобальной функции можно вызывать ОбработкаОшибок.ПодробноеПредставлениеОшибки().

Что обычно ломается

Диагностика: выберите симптом

Частые вопросы

Можно ли программно добавить файл в веб-клиенте без диалога?
Нет. Браузер не даёт коду читать диск пользователя без его выбора. Файл выбирает пользователь в диалоге или перетаскивает на форму.
Есть ли ограничение на размер файла в ХранилищеЗначения?
Жёсткого лимита платформы для практических задач нет, ограничивают память сервера и веб-сервер. Файлы от десятков мегабайт лучше хранить в томах на диске.
Как передать вместе с файлом дополнительные поля через HTTP?
Передайте их параметрами строки запроса или заголовками, а файл оставьте телом. В обработчике читайте их из Запрос.ПараметрыЗапроса или Запрос.Заголовки.
Чем COM-соединение хуже HTTP-сервиса для обмена файлами?
COM работает только под Windows, занимает лицензию на сеанс и требует совпадения версий и разрядности. HTTP-сервис не зависит от платформы отправителя.
Как проверить, что файл не битый после загрузки?
Сравните размер и хеш. На стороне 1С хеш считает объект ХешированиеДанных, например с алгоритмом SHA256; отправитель передаёт свой хеш заголовком.