Файл с диска пользователя на сервер 1С: временное хранилище, код и ошибки

· 9 мин

Содержание

Чтобы поместить файл во временное хранилище 1С, на клиенте вызывают НачатьПомещениеФайла() с уникальным идентификатором формы. Платформа передаёт файл на сервер и возвращает адрес вида e1cib/tempstorage/…. На сервере по этому адресу берут ДвоичныеДанные через ПолучитьИзВременногоХранилища(). Если файл уже лежит на сервере, хватает ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(Путь), УникальныйИдентификатор).

Типичная ситуация: бухгалтер просит кнопку «Загрузить выписку из Excel» в обработке. Форма открыта на клиенте, файл лежит на его компьютере, а разбирать его нужно на сервере. Мост между ними и есть временное хранилище. Отдельной платформенной функции с именем «ПоместитьФайлВоВременноеХранилище» нет: задачу решают связкой из двух-трёх методов, и порядок их вызова важен.

Как устроено временное хранилище 1С и что в нём можно хранить

Временное хранилище — область на стороне сервера 1С, привязанная к сеансу пользователя. Каждое значение в нём доступно по строковому адресу. Адрес можно спокойно передавать между клиентом и сервером: это обычная строка, а сами данные по сети лишний раз не гоняются.

Что можно положить:

  • 📄 ДвоичныеДанные — содержимое любого файла: xlsx, pdf, xml, картинки, архивы;
  • 🧾 ТабличныйДокумент, ТаблицаЗначений, Структура, Массив — любые сериализуемые значения;
  • 🖼 Картинка — например, для показа в поле формы через адрес.

Чего положить нельзя: несериализуемые объекты — ЗаписьXML, открытый ЧтениеТекста, COM-объект, форму.

Сколько живёт значение

Время жизни задаёт второй параметр ПоместитьВоВременноеХранилище():

Что передано вторым параметромСколько живёт значение
УникальныйИдентификатор формыДо закрытия формы
Адрес уже существующего хранилищаЗаменяет значение по этому адресу, срок прежний
НичегоТолько до конца текущего вызова сервера

Без идентификатора формы данные исчезнут после возврата с сервера. Это главная причина ситуации «на клиенте получили адрес, а на сервере по нему пусто». В любом случае значение не переживает сеанс пользователя.

Каким методом поместить файл во временное хранилище

Метод зависит от того, где физически лежит файл и в какой версии платформы вы работаете.

МетодГде вызываетсяКогда подходит
НачатьПомещениеФайла()КлиентОдин файл с компьютера пользователя, классический асинхронный вариант
НачатьПомещениеФайлов()КлиентНесколько файлов за раз
ПоместитьФайлНаСерверАсинх()КлиентПлатформа 8.3.18 и новее: асинхронная модель с Ждать, параметры диалога через ПараметрыДиалогаПомещенияФайлов
ПоместитьВоВременноеХранилище()Клиент и серверФайл уже прочитан в ДвоичныеДанные или лежит на диске сервера
ПоместитьФайл()КлиентУстаревший синхронный метод; при запрете модальности в конфигурации вызывает ошибку

Синтаксис НачатьПомещениеФайла

НачатьПомещениеФайла(ОписаниеОповещения, Адрес, НачальноеИмяФайла, Интерактивно, УникальныйИдентификаторФормы);

Что означает каждый параметр:

  1. ОписаниеОповещения — процедура, которую платформа вызовет после передачи файла.
  2. Адрес — пусто, если нужен новый адрес. Если передать существующий, файл ляжет по нему.
  3. НачальноеИмяФайла — путь, подставленный в диалог, или путь для загрузки без диалога.
  4. Интерактивно — Истина открывает окно выбора файла. Ложь грузит файл по пути без вопросов.
  5. УникальныйИдентификаторФормы — почти всегда УникальныйИдентификатор текущей формы.

Процедура-обработчик получает четыре параметра: Результат (булево — выбран ли файл), Адрес, ВыбранноеИмяФайла и ДополнительныеПараметры. Её обязательно объявляют с Экспорт, иначе оповещение не сработает.

Пример: пользователь выбирает файл, сервер его разбирает

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

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

&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
	Если Не Результат Тогда
		Возврат; // пользователь закрыл диалог
	КонецЕсли;
	КоличествоСтрок = ПрочитатьФайлНаСервере(Адрес, ВыбранноеИмяФайла);
	Сообщить(СтрШаблон("Прочитано строк: %1", Формат(КоличествоСтрок, "ЧГ=0")));
КонецПроцедуры

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

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

	Таблица = Новый ТабличныйДокумент;
	Попытка
		Таблица.Прочитать(ИмяВременногоФайла);
	Исключение
		УдалитьФайлы(ИмяВременногоФайла);
		ВызватьИсключение;
	КонецПопытки;
	УдалитьФайлы(ИмяВременногоФайла);

	Возврат Таблица.ВысотаТаблицы;
КонецФункции

Три приёма, на которых держится этот код:

  • 🔑 Идентификатор формы в пятом параметре. Без него адрес «протухнет» между клиентским и серверным вызовом.
  • 💾 Промежуточный файл на сервере. ТабличныйДокумент.Прочитать(), ЧтениеXML.ОткрытьФайл() и многие другие объекты принимают путь, а не двоичные данные. Поэтому данные пишут в ПолучитьИмяВременногоФайла() и сразу удаляют после чтения — даже если чтение упало.
  • 🧹 Удаление из хранилища сразу после получения. Данные уже в переменной, держать копию в памяти сервера незачем.

В конфигурациях на БСП то же самое делает ФайловаяСистемаКлиент.ЗагрузитьФайл(). Модуль сам выбирает асинхронный метод и учитывает веб-клиент. Если библиотека есть в конфигурации, начните с неё.

Где лежит файл, который нужно поместить во временное хранилище?

Объект ДвоичныеДанные: прочитать, записать, передать дальше

Почти всё, что вы кладёте во временное хранилище при работе с файлами, — это ДвоичныеДанные. Объект хранит содержимое файла целиком в памяти и не знает ни имени, ни расширения. Имя передавайте рядом: отдельным параметром или в структуре.

Основные операции:

ЗадачаКод
Прочитать файл с дискаДанные = Новый ДвоичныеДанные(ПутьКФайлу);
Записать на дискДанные.Записать(ПутьКФайлу);
Узнать размер в байтахДанные.Размер()
Перевести в строку Base64ПолучитьBase64СтрокуИзДвоичныхДанных(Данные)
Получить из строки Base64ПолучитьДвоичныеДанныеИзBase64Строки(Строка)
Читать потокомПоток = Данные.ОткрытьПотокДляЧтения();

Текст и XML можно разобрать без файла на диске

Для текстовых форматов промежуточный файл не нужен. Строку из двоичных данных получают функцией ПолучитьСтрокуИзДвоичныхДанных(Данные, "UTF-8"), а XML читают из строки:

Текст = ПолучитьСтрокуИзДвоичныхДанных(Данные, "UTF-8");
Чтение = Новый ЧтениеXML;
Чтение.УстановитьСтроку(Текст);

Кодировку укажите явно. Выгрузки из старых программ и банковские файлы часто приходят в windows-1251, и без параметра вместо кириллицы получатся «кракозябры».

Как сохранить файл в базе, а не только в хранилище

Временное хранилище живёт до закрытия формы. Если файл нужно сохранить навсегда — например, скан договора у контрагента, — переложите данные в реквизит типа ХранилищеЗначения:

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

Здесь Скан — реквизит справочника с типом ХранилищеЗначения. Обратно данные достают методом Получить(). Хранить в реквизите сам адрес временного хранилища бессмысленно: после закрытия формы он ведёт в пустоту.

Файловый вариант, клиент-сервер и веб-клиент: где на самом деле лежит файл

Код выше одинаково работает во всех вариантах. Различается другое — чья файловая система видна в серверной процедуре.

  • 🗂 Файловая база. Серверная часть выполняется на том же компьютере, что и клиент. Поэтому Новый ДвоичныеДанные("C:\Выгрузка\file.xlsx") в серверной процедуре «случайно» работает. Перенесите базу на сервер 1С — и этот код сломается.
  • 🖥 Клиент-серверная база. Серверный код выполняется рабочим процессом на сервере 1С. Путь C:\… в нём — это диск сервера, а не компьютера бухгалтера. Права на чтение — у пользователя Windows, от которого запущена служба сервера 1С, а не у пользователя 1С.
  • 🌐 Веб-клиент. Интерактивная загрузка через диалог работает без расширения. Загрузка по известному пути (Интерактивно = Ложь) требует расширения работы с файлами: его подключают через НачатьПодключениеРасширенияРаботыСФайлами(), а при первом запуске — устанавливают через НачатьУстановкуРасширенияРаботыСФайлами().

Сетевую папку на сервере указывайте UNC-путём (\\fileserver\obmen\), а не буквой подключённого диска. Буква диска существует только в сеансе конкретного пользователя Windows, а служба 1С её не видит.

Фоновое задание и временное хранилище

Фоновое задание выполняется в отдельном сеансе. Надёжная схема такая: адрес для результата создают заранее в сеансе формы и передают его в задание параметром.

АдресРезультата = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор);
Параметры = Новый Массив;
Параметры.Добавить(ДанныеФайла);      // сами ДвоичныеДанные
Параметры.Добавить(АдресРезультата);
ФоновыеЗадания.Выполнить("ОбработкаФайлов.РазобратьФайл", Параметры);

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

Проверки и обработка ошибок при помещении файла в хранилище

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

Проверки перед чтением

  1. Файл на сервере существует. Новый Файл(Путь).Существует() — до создания ДвоичныеДанные, иначе получите исключение доступа к файлу.
  2. Адрес корректен. ЭтоАдресВременногоХранилища(Адрес) возвращает Истина, если строка похожа на адрес хранилища. Защищает от пустой строки и мусора в параметре.
  3. Данные на месте. ПолучитьИзВременногоХранилища() возвращает Неопределено, если значение уже удалено или срок его жизни истёк. Проверяйте тип результата до обращения к методам.
  4. Размер разумный. Данные.Размер() до разбора: файл на 300 МБ лучше отклонить сразу, чем занять память рабочего процесса.
  5. Расширение ожидаемое. Берите его из ВыбранноеИмяФайла через Новый Файл(ИмяФайла).Расширение и сравнивайте в нижнем регистре.

Шаблон серверной проверки

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

	Данные = ПолучитьИзВременногоХранилища(Адрес);
	Если ТипЗнч(Данные) <> Тип("ДвоичныеДанные") Тогда
		ВызватьИсключение "Файл не найден во временном хранилище. Загрузите его повторно.";
	КонецЕсли;

	РазмерМБ = Данные.Размер() / 1024 / 1024;
	Если РазмерМБ > МаксимумМБ Тогда
		ВызватьИсключение СтрШаблон("Файл весит %1 МБ, допустимо не более %2 МБ.",
			Формат(РазмерМБ, "ЧДЦ=1"), Формат(МаксимумМБ, "ЧГ=0"));
	КонецЕсли;

	Возврат Данные;
КонецФункции

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

Когда и как очищать временное хранилище

Платформа сама удалит значения при закрытии формы или по окончании сеанса. Но на этот механизм не стоит полагаться в трёх случаях.

  • 📦 Большие файлы. Пока форма открыта, данные занимают память сервера. Пользователь загрузил пять архивов по 100 МБ и ушёл на обед — память держится всё это время.
  • 🔁 Повторная загрузка в той же форме. Каждый вызов без адреса создаёт новое значение, старые копятся до закрытия формы. Передавайте прежний адрес вторым параметром — новое значение заменит старое.
  • 🖥 Долгоживущие формы. Рабочее место, которое открыто весь день, накопит всё, что через него прошло.

Правило простое: забрали данные на сервере — сразу вызовите УдалитьИзВременногоХранилища(Адрес). Если адрес хранится в реквизите формы, после удаления очистите и реквизит, чтобы повторный вызов не обратился к пустому адресу.

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

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

Видят ли другие пользователи то, что я положил во временное хранилище?
Нет. Хранилище привязано к сеансу: по чужому адресу из другого сеанса данные не получить. Для обмена между пользователями сохраняйте файл в базу.
Можно ли поместить во временное хранилище папку целиком?
Нет, только отдельные файлы. Либо загрузите их по одному через НачатьПомещениеФайлов, либо упакуйте папку в zip на клиенте и передайте архив.
Почему картинка из временного хранилища не показывается в поле формы?
Поле картинки принимает адрес, но значение по нему должно жить, пока открыта форма. Помещайте картинку с идентификатором формы, а не без второго параметра.
Есть ли у временного хранилища ограничение по размеру?
Жёсткий лимит в коде не задан, упираетесь вы в память рабочего процесса сервера и время передачи по сети. Для больших файлов ставьте собственный порог проверки размера.
Как отдать пользователю файл, сформированный на сервере?
Обратным путём: на сервере ПоместитьВоВременноеХранилище, адрес вернуть на клиент, там вызвать асинхронный метод получения файла или ФайловаяСистемаКлиент.СохранитьФайл из БСП.