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

· 9 мин

Содержание

Временный файл в 1С создают в два хода: функция ПолучитьИмяВременногоФайла("xlsx") возвращает уникальный путь в каталоге временных файлов, а сам файл появляется на диске, когда вы что-то по этому пути записываете. После работы файл удаляют вызовом УдалитьФайлы(ИмяФайла).

Типичная ситуация: пользователь загружает выписку в Excel, сервер должен её разобрать. Метод ТабличныйДокумент.Прочитать() работает с файлом, а не с двоичными данными. Значит, нужен временный файл: записать, прочитать, удалить. И сделать это так, чтобы не словить ошибку совместного доступа и не оставить гигабайты мусора в Temp сервера.

Как устроены временный файл и временное хранилище

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

МеханизмГде живут данныеЧем создаётсяДля чего
Временный файлДиск компьютера, где выполняется кодПолучитьИмяВременногоФайла() + записьМетоды, которым нужен путь к файлу: чтение Excel, архивы ZIP, внешние программы
Временное хранилищеПамять сеанса на сервере 1СПоместитьВоВременноеХранилище()Передача данных между клиентом и сервером, между вызовами сервера

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

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

💡 Каталог, куда платформа складывает временные файлы, возвращает функция КаталогВременныхФайлов(). На клиенте Windows это обычно %TEMP% пользователя. На сервере — каталог Temp той учётной записи, под которой запущена служба сервера 1С (часто это USR1CV8).

Создание временного файла и получение его имени

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

ИмяФайла = ПолучитьИмяВременногоФайла("xlsx");
// Получится путь вида C:\Users\...\AppData\Local\Temp\v8_....xlsx

Что нужно знать про эту функцию:

  • 📄 Она не создаёт файл. Она только возвращает свободное имя. Файл появится после первой записи.
  • 🔀 Имя уникально в момент вызова. Два вызова подряд дадут два разных пути.
  • 🏷️ Без параметра расширение будет tmp. Для Excel, XML, ZIP указывайте реальное расширение: некоторые методы определяют формат по нему.
  • 🖥️ Работает на сервере, в толстом и тонком клиенте, во внешнем соединении. В веб-клиенте её нет.

Когда нужен временный каталог

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

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

Уникальное имя берут той же функцией, а СоздатьКаталог() превращает его в папку. Удаляется каталог вместе с содержимым через УдалитьФайлы(ИмяКаталога).

Обёртки БСП

В типовых конфигурациях на БСП 3.x есть общий модуль ФайловаяСистема. В нём функции ВременныйФайл(Расширение), СоздатьВременныйКаталог() и процедуры удаления УдалитьВременныйФайл(), УдалитьВременныйКаталог(). По сути это те же вызовы платформы плюс обработка ошибок и запись в журнал регистрации. Если дорабатываете типовую конфигурацию , пользуйтесь ими: так код единообразен с остальной базой.

Запись данных во временный файл

Способ записи зависит от того, что у вас на руках.

Готовые двоичные данные

Самый частый случай — данные пришли из временного хранилища, из HTTP-ответа или из реквизита типа ХранилищеЗначения:

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

Метод ДвоичныеДанные.Записать() создаёт файл и сразу закрывает его. Блокировок после него не остаётся.

Текст построчно

Для CSV, логов, текстовых выгрузок подходит объект ЗаписьТекста:

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

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

Структурированные форматы

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

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

Табличный документ и текстовый документ пишут себя сами: ТабДок.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX), ТекстДок.Записать(ИмяФайла, КодировкаТекста.UTF8).

Через потоки

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

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

Объекты ФайловыйПоток, ЗаписьДанных и ЧтениеДанных появились в платформе 8.3.9. В базах на старых платформах их не будет.

Какой способ записи выбрать

Что на входеСпособКогда не подходит
ДвоичныеДанныеДанныеФайла.Записать(ИмяФайла)Файл в сотни мегабайт: данные уже целиком в памяти
Строки, CSV, логЗаписьТекстаНужен XML или JSON со структурой
Структура, массивЗаписьJSON, ЗаписьXMLФормат обмена задан другой стороной иначе
Отчёт, печатная формаТабДок.Записать()Нужен «сырой» текст без оформления
Большой поток данныхФайловыеПотоки + КопироватьВ()Платформа ниже 8.3.9

Чтение и разбор содержимого временного файла

Чтение — зеркало записи. Текст читают построчно до Неопределено:

Чтение = Новый ЧтениеТекста(ИмяФайла, КодировкаТекста.UTF8);
Строка = Чтение.ПрочитатьСтроку();
Пока Строка <> Неопределено Цикл
    Части = СтрРазделить(Строка, ";");
    // Части[0] — артикул, Части[1] — количество
    Строка = Чтение.ПрочитатьСтроку();
КонецЦикла;
Чтение.Закрыть();

JSON — через ЧтениеJSON:

ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.ОткрытьФайл(ИмяФайла);
Данные = ПрочитатьJSON(ЧтениеJSON);
ЧтениеJSON.Закрыть();

Полный пример: загрузка Excel на сервере

Это главный практический сценарий. Пользователь выбирает файл на клиенте, сервер разбирает его через временный файл.

Клиентская часть (работает и в тонком, и в веб-клиенте):

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

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

Серверная часть:

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

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

    Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
        Артикул = ТабДок.Область(НомерСтроки, 1, НомерСтроки, 1).Текст;
        Количество = ТабДок.Область(НомерСтроки, 2, НомерСтроки, 2).Текст;
        // дальше — поиск номенклатуры по артикулу и заполнение табличной части
    КонецЦикла;

    УдалитьИзВременногоХранилища(Адрес);
КонецПроцедуры

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

Очистка и удаление временных данных

Платформа не обещает убрать за вами временные файлы. Если код их не удаляет, каталог Temp на сервере растёт месяцами. На загруженном сервере это десятки тысяч файлов и гигабайты.

Правила уборки:

  1. ✅ Удаляйте файл в той же процедуре, где создали, сразу после последнего обращения.
  2. ✅ Оборачивайте работу с файлом в Попытка … Исключение: при ошибке разбора файл тоже должен удалиться.
  3. ✅ Перед удалением закрывайте все объекты чтения и записи (Закрыть()).
  4. ✅ Само удаление тоже ставьте в Попытка: если файл занят антивирусом, ошибка удаления не должна прерывать основную операцию.
Попытка
    УдалитьФайлы(ИмяФайла);
Исключение
    ЗаписьЖурналаРегистрации("Удаление временного файла",
        УровеньЖурналаРегистрации.Предупреждение, , ,
        ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;

Временное хранилище тоже чистят

Срок жизни значения в хранилище зависит от второго параметра ПоместитьВоВременноеХранилище():

  • 🗂️ передан уникальный идентификатор формы — значение живёт, пока открыта форма;
  • ⏱️ параметр не указан — значение удаляется после очередного серверного вызова.

Если положили в хранилище большой файл с идентификатором формы, а форма висит открытой весь день, данные весь день занимают память. Освобождайте её явно: УдалитьИзВременногоХранилища(Адрес).

Чистить Temp сервера вручную можно, но не во время работы пользователей: вы удалите файлы, с которыми прямо сейчас работают сеансы, и у кого-то оборвётся загрузка или формирование отчёта. Делайте это вне рабочего времени. Файлы v8_*, которым больше суток, обычно и есть забытый мусор.

Временный файл в тонком клиенте и веб-клиенте

Поведение кода зависит от того, где он выполняется.

Где выполняется кодПолучитьИмяВременногоФайла()Что учесть
Сервер (&НаСервере, общие модули, фоновые задания)ДоступнаФайл на диске сервера, права учётной записи службы
Тонкий клиентДоступнаФайл на компьютере пользователя; при запрете синхронных вызовов используйте асинхронные методы
Толстый клиентДоступнаКак тонкий клиент
Веб-клиентНедоступнаПрямого доступа к диску нет

Тонкий клиент

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

Если в свойствах конфигурации режим использования синхронных вызовов установлен в «Не использовать», синхронные файловые методы на клиенте вызовут ошибку. Тогда вместо УдалитьФайлы() применяют НачатьУдалениеФайлов() с описанием оповещения, а в платформе 8.3.18 и новее — методы с суффиксом Асинх вроде УдалитьФайлыАсинх() и КаталогВременныхФайловАсинх() с оператором Ждать.

Веб-клиент

Браузер не пускает код к файловой системе. Поэтому в веб-клиенте:

  • 🚫 нет ПолучитьИмяВременногоФайла() и синхронного КаталогВременныхФайлов();
  • 🧩 для работы с локальными файлами нужно расширение работы с файлами (подключают через НачатьУстановкуРасширенияРаботыСФайлами()), а каталог получают асинхронно;
  • ✅ универсальный путь без расширения — вся обработка на сервере.

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

Подводные камни на сервере 1С

Две проблемы встречаются в клиент-серверных базах и не воспроизводятся в файловой.

Файл «пропал» между вызовами сервера

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

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

Нет прав на каталог

Служба сервера 1С пишет в Temp своей учётной записи. Если администратор ограничил права этой учётной записи или указал для неё чужой каталог, вызов Записать() упадёт с ошибкой доступа. Проверьте, под кем запущена служба, и что у этой учётной записи есть права на запись в её каталог временных файлов.

Ещё одно ограничение: в безопасном режиме (внешние обработки, расширения с флагом безопасного режима) доступ к файловой системе по умолчанию запрещён. Разрешают его профилем безопасности на кластере.

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

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

Можно ли задать своё имя временного файла?
Можно: склеить КаталогВременныхФайлов() и имя, например "выгрузка.xml". Но тогда уникальность на вас: два пользователя на одном сервере перезапишут файлы друг друга. Если имя нужно для пользователя, задавайте его при сохранении, а на диске держите уникальное.
Удаляет ли платформа временные файлы сама?
Полагаться на это нельзя. Каталог Temp сервера, где код не убирает за собой, со временем разрастается до гигабайтов. Удаляйте файлы явно.
Работает ли ПолучитьИмяВременногоФайла в фоновом задании?
Да. Фоновое задание выполняется на сервере, файл создаётся в каталоге временных файлов службы. Результат в форму передавайте через временное хранилище, а не путём к файлу.
Нужен ли временный файл, чтобы прочитать XML из хранилища?
Не обязательно. У ЧтениеXML есть метод УстановитьСтроку(), а двоичные данные можно читать потоком через ОткрытьПотокДляЧтения(). Временный файл нужен методам, которые принимают только путь, например ТабличныйДокумент.Прочитать().
Где на сервере искать забытые временные файлы?
Выполните на сервере КаталогВременныхФайлов() и выведите результат в журнал регистрации или сообщение. Это и есть каталог, где копятся файлы v8_*.