Временные файлы в 1С 8.3: имя, запись, чтение и уборка за собой
Содержание
Временный файл в 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 на сервере растёт месяцами. На загруженном сервере это десятки тысяч файлов и гигабайты.
Правила уборки:
- ✅ Удаляйте файл в той же процедуре, где создали, сразу после последнего обращения.
- ✅ Оборачивайте работу с файлом в
Попытка … Исключение: при ошибке разбора файл тоже должен удалиться. - ✅ Перед удалением закрывайте все объекты чтения и записи (
Закрыть()). - ✅ Само удаление тоже ставьте в
Попытка: если файл занят антивирусом, ошибка удаления не должна прерывать основную операцию.
Попытка
УдалитьФайлы(ИмяФайла);
Исключение
ЗаписьЖурналаРегистрации("Удаление временного файла",
УровеньЖурналаРегистрации.Предупреждение, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
Временное хранилище тоже чистят
Срок жизни значения в хранилище зависит от второго параметра ПоместитьВоВременноеХранилище():
- 🗂️ передан уникальный идентификатор формы — значение живёт, пока открыта форма;
- ⏱️ параметр не указан — значение удаляется после очередного серверного вызова.
Если положили в хранилище большой файл с идентификатором формы, а форма висит открытой весь день, данные весь день занимают память. Освобождайте её явно: УдалитьИзВременногоХранилища(Адрес).
Чистить Temp сервера вручную можно, но не во время работы пользователей: вы удалите файлы, с которыми прямо сейчас работают сеансы, и у кого-то оборвётся загрузка или формирование отчёта. Делайте это вне рабочего времени. Файлы v8_*, которым больше суток, обычно и есть забытый мусор.
Временный файл в тонком клиенте и веб-клиенте
Поведение кода зависит от того, где он выполняется.
| Где выполняется код | ПолучитьИмяВременногоФайла() | Что учесть |
|---|---|---|
Сервер (&НаСервере, общие модули, фоновые задания) | Доступна | Файл на диске сервера, права учётной записи службы |
| Тонкий клиент | Доступна | Файл на компьютере пользователя; при запрете синхронных вызовов используйте асинхронные методы |
| Толстый клиент | Доступна | Как тонкий клиент |
| Веб-клиент | Недоступна | Прямого доступа к диску нет |
Тонкий клиент
В тонком клиенте временный файл создают, когда нужно открыть файл во внешней программе или передать его в другую систему на компьютере пользователя.
Если в свойствах конфигурации режим использования синхронных вызовов установлен в «Не использовать», синхронные файловые методы на клиенте вызовут ошибку. Тогда вместо УдалитьФайлы() применяют НачатьУдалениеФайлов() с описанием оповещения, а в платформе 8.3.18 и новее — методы с суффиксом Асинх вроде УдалитьФайлыАсинх() и КаталогВременныхФайловАсинх() с оператором Ждать.
Веб-клиент
Браузер не пускает код к файловой системе. Поэтому в веб-клиенте:
- 🚫 нет
ПолучитьИмяВременногоФайла()и синхронногоКаталогВременныхФайлов(); - 🧩 для работы с локальными файлами нужно расширение работы с файлами (подключают через
НачатьУстановкуРасширенияРаботыСФайлами()), а каталог получают асинхронно; - ✅ универсальный путь без расширения — вся обработка на сервере.
Рабочая схема для веб-клиента та же, что в примере с Excel: НачатьПомещениеФайла() в интерактивном режиме → адрес во временном хранилище → временный файл на сервере. Для отдачи результата пользователю сервер кладёт двоичные данные в хранилище, а клиент сохраняет их по адресу. В типовых конфигурациях для этого есть ФайловаяСистемаКлиент.СохранитьФайл().
Подводные камни на сервере 1С
Две проблемы встречаются в клиент-серверных базах и не воспроизводятся в файловой.
Файл «пропал» между вызовами сервера
В кластере серверов 1С несколько рабочих процессов, иногда на разных машинах. Два последовательных серверных вызова из одной формы могут обработать разные процессы. Вы сохранили путь к временному файлу в реквизит формы, а при следующем вызове файла по этому пути нет: он остался на другом сервере.
Правило простое: создавайте, используйте и удаляйте временный файл в рамках одного серверного вызова. Между вызовами передавайте двоичные данные через временное хранилище, а не путь к файлу.
Нет прав на каталог
Служба сервера 1С пишет в Temp своей учётной записи. Если администратор ограничил права этой учётной записи или указал для неё чужой каталог, вызов Записать() упадёт с ошибкой доступа. Проверьте, под кем запущена служба, и что у этой учётной записи есть права на запись в её каталог временных файлов.
Ещё одно ограничение: в безопасном режиме (внешние обработки, расширения с флагом безопасного режима) доступ к файловой системе по умолчанию запрещён. Разрешают его профилем безопасности на кластере.
Частые вопросы
Можно ли задать своё имя временного файла?
КаталогВременныхФайлов() и имя, например "выгрузка.xml". Но тогда уникальность на вас: два пользователя на одном сервере перезапишут файлы друг друга. Если имя нужно для пользователя, задавайте его при сохранении, а на диске держите уникальное.Удаляет ли платформа временные файлы сама?
Temp сервера, где код не убирает за собой, со временем разрастается до гигабайтов. Удаляйте файлы явно.Работает ли ПолучитьИмяВременногоФайла в фоновом задании?
Нужен ли временный файл, чтобы прочитать XML из хранилища?
ЧтениеXML есть метод УстановитьСтроку(), а двоичные данные можно читать потоком через ОткрытьПотокДляЧтения(). Временный файл нужен методам, которые принимают только путь, например ТабличныйДокумент.Прочитать().Где на сервере искать забытые временные файлы?
КаталогВременныхФайлов() и выведите результат в журнал регистрации или сообщение. Это и есть каталог, где копятся файлы v8_*.