Временные файлы в 1С 8.3: создание, запись, передача и удаление

· 9 мин

Содержание

Временный файл в 1С записывают в два шага: получают уникальное имя через ПолучитьИмяВременногоФайла("txt"), затем пишут в него нужным объектом — ЗаписьТекста, ДвоичныеДанные.Записать(), ЗаписьJSON или ФайловыйПоток. После использования файл удаляют командой УдалитьФайлы(), платформа сама этого не делает.

Типичная ситуация: обработка выгружает реестр платежей для банка или собирает PDF для отправки по почте. Хранить такой файл постоянно незачем, а положить его «куда-нибудь на диск C:» нельзя — на сервере у службы 1С может не быть туда доступа. Для этого и нужен временный каталог. Ниже — код для платформы 8.3 и управляемых форм, общий для всех конфигураций: БП, ЗУП, УТ и других.

Где лежат временные файлы и как получить путь к ним

У платформы два метода глобального контекста для работы с временным каталогом:

МетодЧто возвращаетСоздаёт ли файл
КаталогВременныхФайлов()Путь к каталогу временных файлов с разделителем в концеНет
ПолучитьИмяВременногоФайла("ext")Полный путь к файлу с уникальным именем и заданным расширениемНет, только имя

Расширение передают без точки: "xlsx", а не ".xlsx". Если параметр пустой, имя получится с расширением .tmp.

ПолучитьИмяВременногоФайла не создаёт файл, а лишь гарантирует уникальное имя. Файл появится на диске после первой записи.

Какой это каталог, зависит от того, где выполняется код:

  • 🖥️ на клиенте (тонкий или толстый клиент под Windows) — временный каталог текущего пользователя, обычно %TEMP%, то есть C:\Users\<пользователь>\AppData\Local\Temp;
  • 🗄️ на сервере 1С — временный каталог учётной записи, под которой запущена служба агента сервера. Для стандартной учётки USR1CV8 это её профиль, а не профиль того, кто работает в базе;
  • 🐧 сервер на Linux — как правило, /tmp или каталог из переменной TMPDIR пользователя службы;
  • 📁 файловая база — код с директивой &НаСервере выполняется на том же компьютере, что и клиент, поэтому каталог тот же.

Если нужен не один файл, а набор (например, распаковать архив), создайте свой подкаталог с уникальным именем:

ВременныйКаталог = КаталогВременныхФайлов() + Строка(Новый УникальныйИдентификатор);
СоздатьКаталог(ВременныйКаталог);

Удалить его потом можно одной командой вместе со всем содержимым — об этом ниже.

Как записать временный файл в 1С: текст, XML, JSON, табличный документ

Способ записи выбирают по типу данных. Во всех примерах сначала получаем имя, потом пишем.

Текстовый файл через ЗаписьТекста

Подходит для CSV, логов, файлов обмена в простом формате. Объект пишет построчно и не держит весь текст в памяти.

ИмяФайла = ПолучитьИмяВременногоФайла("csv");

Запись = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
Запись.ЗаписатьСтроку("ИНН;Контрагент;Сумма");
Для Каждого Строка Из ТаблицаДанных Цикл
    Запись.ЗаписатьСтроку(СтрШаблон("%1;%2;%3",
        Строка.ИНН, Строка.Наименование, Формат(Строка.Сумма, "ЧДЦ=2; ЧРД=.; ЧГ=0")));
КонецЦикла;
Запись.Закрыть();

Четвёртый параметр конструктора — «дописывать». Если передать Истина, строки добавятся в конец существующего файла:

Запись = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8, , Истина);

Всегда вызывайте Закрыть(). Пока объект не закрыт, файл заблокирован: его нельзя прочитать другим объектом, передать в двоичные данные или удалить.

Текстовый файл через ТекстовыйДокумент

Удобен, когда текст сначала собирают целиком, а потом сохраняют одной командой:

Текст = Новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("Реестр сформирован: " + Формат(ТекущаяДатаСеанса(), "ДФ=dd.MM.yyyy"));
Текст.ДобавитьСтроку("Количество строк: " + Формат(ТаблицаДанных.Количество(), "ЧГ=0"));
Текст.Записать(ИмяФайла, КодировкаТекста.ANSI);

Кодировку выбирают под получателя. Старые банковские программы часто ждут Windows-1251 (КодировкаТекста.ANSI), веб-сервисы — UTF-8.

XML и JSON

Объекты ЗаписьXML и ЗаписьJSON открывают файл сами методом ОткрытьФайл:

ИмяФайла = ПолучитьИмяВременногоФайла("json");

ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.ОткрытьФайл(ИмяФайла);
ЗаписатьJSON(ЗаписьJSON, СтруктураДанных);
ЗаписьJSON.Закрыть();

Для XML порядок тот же: ОткрытьФайл(ИмяФайла), затем ЗаписатьОбъявлениеXML(), элементы и Закрыть().

Табличный документ в Excel или PDF

Печатную форму или отчёт сохраняют методом Записать с указанием типа файла:

ИмяФайла = ПолучитьИмяВременногоФайла("pdf");
ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.PDF);

Для Excel используйте ТипФайлаТабличногоДокумента.XLSX и расширение "xlsx". Расширение в имени и тип в методе должны совпадать, иначе программа на стороне получателя может не открыть файл.

Запись двоичных данных и работа с потоками

Двоичные данные — это содержимое любого файла «как есть»: картинка, архив, файл из присоединённых файлов, ответ HTTP-сервиса.

Сохранить ДвоичныеДанные на диск

Самый короткий путь:

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

Обратная операция — прочитать файл в память: Новый ДвоичныеДанные(ИмяФайла).

Запись через ФайловыйПоток и ЗаписьДанных

ДвоичныеДанные целиком лежат в оперативной памяти. Для файла в сотни мегабайт это лишняя нагрузка на рабочий процесс. Потоки пишут порциями:

ИмяФайла = ПолучитьИмяВременногоФайла("txt");

Поток = Новый ФайловыйПоток(ИмяФайла, РежимОткрытияФайла.Создать, ДоступКФайлу.Запись);
Запись = Новый ЗаписьДанных(Поток);
Запись.ЗаписатьСтроку("Первая строка", КодировкаТекста.UTF8);
Запись.ЗаписатьСтроку("Вторая строка", КодировкаТекста.UTF8);
Запись.Сбросить();
Поток.Закрыть();

ЗаписьДанных пишет байты ровно так, как вы их передали. Этим пользуются, когда получатель не принимает метку BOM в начале UTF-8 файла, которую добавляет ЗаписьТекста.

Скопировать двоичные данные в файл без промежуточного буфера можно так:

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

Когда файл вообще не нужен

Часто временный файл заводят по привычке, хотя данные можно собрать в памяти. ПотокВПамяти даёт тот же интерфейс, что и файловый поток, но без диска:

Поток = Новый ПотокВПамяти;
Запись = Новый ЗаписьДанных(Поток);
Запись.ЗаписатьСтроку("Данные для отправки", КодировкаТекста.UTF8);
Запись.Сбросить();
ДанныеФайла = Поток.ЗакрытьИПолучитьДвоичныеДанные();

Нет файла — нет проблем с правами, блокировками и мусором в каталоге.

СпособКогда выбиратьОграничения
ЗаписьТекстаПострочный текст, CSV, логДобавляет BOM для UTF-8
ТекстовыйДокументТекст небольшого объёма целикомВесь текст в памяти
ДвоичныеДанные.ЗаписатьГотовое содержимое файлаВесь файл в памяти
ФайловыйПоток + ЗаписьДанныхБольшие файлы, точный контроль байтовБольше кода
ПотокВПамятиФайл нужен только как данные для передачиНе для очень больших объёмов

Временный файл на сервере и на клиенте: как передать между ними

Главная ловушка клиент-серверной базы: клиент и сервер — разные компьютеры. Файл, записанный в процедуре с &НаСервере, лежит на диске сервера 1С. Пользователь его не видит, а код на клиенте по этому пути ничего не найдёт.

Передают данные не путём к файлу, а через временное хранилище.

С сервера на клиент

Серверная часть кладёт содержимое файла в хранилище и возвращает адрес:

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

Здесь СформироватьТабличныйДокумент() — ваша функция, которая готовит отчёт. На клиенте по адресу сохраняют файл асинхронными методами платформы. В конфигурациях на БСП удобнее вызвать ФайловаяСистемаКлиент.СохранитьФайл: он сам покажет диалог и учтёт веб-клиент.

С клиента на сервер

Обратный путь: пользователь выбирает файл, клиент помещает его во временное хранилище (в БСП — ФайловаяСистемаКлиент.ЗагрузитьФайл), сервер получает адрес. Дальше на сервере:

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

Одно правило для серверного кода

Создавайте, используйте и удаляйте временный файл в рамках одного серверного вызова. В кластере из нескольких рабочих серверов следующий вызов может попасть на другой компьютер, и файла по сохранённому пути там не окажется. Ошибка проявляется «через раз», и её долго ищут. Между вызовами передавайте адрес во временном хранилище, а не путь.

В веб-клиенте прямого доступа к файловой системе нет. Для работы с файлами на компьютере пользователя браузеру нужно расширение работы с файлами; без него остаются только диалоги выбора и сохранения.

Удаление временных файлов и освобождение ресурсов

Платформа не удаляет файлы, имена которых выдала ПолучитьИмяВременногоФайла. Если не убирать за собой, каталог Temp на сервере за год обрастает тысячами файлов вида v8_….tmp, а при выгрузках вложений — гигабайтами.

Порядок освобождения ресурсов:

  1. Закройте все объекты, которые держат файл: ЗаписьТекста.Закрыть(), ФайловыйПоток.Закрыть(), ЗаписьXML.Закрыть().
  2. Удалите файл: УдалитьФайлы(ИмяФайла).
  3. Если создавали подкаталог — удалите его целиком: УдалитьФайлы(ВременныйКаталог). Метод снимает каталог вместе с содержимым.
  4. Если клали данные во временное хранилище с адресом без привязки к форме — освободите его: УдалитьИзВременногоХранилища(Адрес).

Конструкции «finally» во встроенном языке нет. Поэтому удаление ставят и после основного кода, и в блок Исключение, иначе при сбое файл останется:

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

Вспомогательная процедура не даёт ошибке удаления затереть исходную ошибку:

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

В БСП есть готовые функции модуля ФайловаяСистема для имени временного файла, временного каталога и их удаления — в типовых конфигурациях лучше пользоваться ими, а не писать свои.

Обработка ошибок при записи временного файла

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

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

Чистить каталог Temp на работающем сервере вручную стоит только в нерабочее время. Файлы, которые прямо сейчас открыты рабочими процессами, не удалятся, а выгрузка у пользователя, попавшего на чистку, может прерваться.

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

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

Можно ли задать своё имя временного файла, а не случайное?
Можно: соберите путь из КаталогВременныхФайлов() и своего имени. Но уникальность тогда на вас — при одновременной работе двух пользователей файлы перезапишут друг друга. Надёжнее создать уникальный подкаталог и положить в него файл с нужным именем.
Почему в файле CSV из 1С кракозябры при открытии в Excel?
Excel по умолчанию читает CSV в кодировке Windows-1251. Либо пишите в КодировкаТекста.ANSI, либо оставляйте UTF-8 с меткой BOM: по ней Excel узнаёт кодировку.
Удаляются ли временные файлы при завершении сеанса?
Файлы, имена которых выдал ПолучитьИмяВременногоФайла, — нет. Удаляет только ваш код. Временное хранилище, привязанное к уникальному идентификатору формы, очищается при закрытии формы.
Как узнать, куда фактически пишет сервер?
Выполните на сервере КаталогВременныхФайлов() и выведите результат сообщением или в журнал регистрации. Путь покажет профиль пользователя, под которым работает служба 1С.
Чем временный файл лучше временного хранилища?
Файл нужен, когда с данными работает метод, который понимает только путь: чтение табличного документа, распаковка ZIP, внешняя программа. Если данные просто передаются между клиентом и сервером, хранилища достаточно.