Временные файлы в 1С 8.3: создание, запись, передача и удаление
Содержание
Временный файл в 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, а при выгрузках вложений — гигабайтами.
Порядок освобождения ресурсов:
- Закройте все объекты, которые держат файл:
ЗаписьТекста.Закрыть(),ФайловыйПоток.Закрыть(),ЗаписьXML.Закрыть(). - Удалите файл:
УдалитьФайлы(ИмяФайла). - Если создавали подкаталог — удалите его целиком:
УдалитьФайлы(ВременныйКаталог). Метод снимает каталог вместе с содержимым. - Если клали данные во временное хранилище с адресом без привязки к форме — освободите его:
УдалитьИзВременногоХранилища(Адрес).
Конструкции «finally» во встроенном языке нет. Поэтому удаление ставят и после основного кода, и в блок Исключение, иначе при сбое файл останется:
ИмяФайла = ПолучитьИмяВременногоФайла("xml");
Попытка
ВыгрузитьДанные(ИмяФайла);
Результат = Новый ДвоичныеДанные(ИмяФайла);
УдалитьВременныйФайл(ИмяФайла);
Исключение
УдалитьВременныйФайл(ИмяФайла);
ВызватьИсключение;
КонецПопытки;
Вспомогательная процедура не даёт ошибке удаления затереть исходную ошибку:
Процедура УдалитьВременныйФайл(ИмяФайла)
Попытка
УдалитьФайлы(ИмяФайла);
Исключение
ЗаписьЖурналаРегистрации("ВременныеФайлы", УровеньЖурналаРегистрации.Предупреждение, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
В БСП есть готовые функции модуля ФайловаяСистема для имени временного файла, временного каталога и их удаления — в типовых конфигурациях лучше пользоваться ими, а не писать свои.
Обработка ошибок при записи временного файла
Ошибки с временными файлами почти всегда приходят в виде Ошибка при вызове метода контекста (Записать) или того же текста с именем другого метода. Подробности — во второй части сообщения. Разбор по симптомам:
Чистить каталог Temp на работающем сервере вручную стоит только в нерабочее время. Файлы, которые прямо сейчас открыты рабочими процессами, не удалятся, а выгрузка у пользователя, попавшего на чистку, может прерваться.
При перехвате ошибки пишите в журнал регистрации полный текст, а пользователю показывайте короткий. ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()) даёт стек вызовов, КраткоеПредставлениеОшибки — понятное сообщение без строк кода.
Частые вопросы
Можно ли задать своё имя временного файла, а не случайное?
КаталогВременныхФайлов() и своего имени. Но уникальность тогда на вас — при одновременной работе двух пользователей файлы перезапишут друг друга. Надёжнее создать уникальный подкаталог и положить в него файл с нужным именем.Почему в файле CSV из 1С кракозябры при открытии в Excel?
КодировкаТекста.ANSI, либо оставляйте UTF-8 с меткой BOM: по ней Excel узнаёт кодировку.Удаляются ли временные файлы при завершении сеанса?
ПолучитьИмяВременногоФайла, — нет. Удаляет только ваш код. Временное хранилище, привязанное к уникальному идентификатору формы, очищается при закрытии формы.Как узнать, куда фактически пишет сервер?
КаталогВременныхФайлов() и выведите результат сообщением или в журнал регистрации. Путь покажет профиль пользователя, под которым работает служба 1С.