Запись файлов из кода 1С:Предприятия 8.3 — текст, двоичные данные, потоки
Содержание
Записать файл в 1С программно проще всего так: на сервере собрать содержимое и вызвать Записать(ИмяФайла) у объекта ТекстовыйДокумент, ДвоичныеДанные или закрыть объект ЗаписьТекста. Для файла на компьютере пользователя схема другая: данные кладутся во временное хранилище, а клиент забирает их и сохраняет через диалог.
Почти все проблемы с записью файлов в 1С возникают не из-за синтаксиса, а из-за того, что код выполняется не там, где думает разработчик. Файл, записанный на сервере по пути C:\Temp\, окажется на диске сервера 1С, а не у бухгалтера. Ниже — рабочие примеры для каждого случая и разбор ошибок.
Где выполняется код и куда попадёт файл
Перед тем как писать первую строку, ответьте на два вопроса: на каком компьютере выполняется процедура и под какой учётной записью.
| Где выполняется код | Куда пишется файл | Под какой учётной записью |
|---|---|---|
&НаСервере, файловая база | диск компьютера, где открыта база | пользователь Windows, запустивший 1С |
&НаСервере, клиент-серверная база | диск сервера 1С | учётная запись службы сервера 1С (часто USR1CV8) |
&НаКлиенте, тонкий или толстый клиент | диск компьютера пользователя | пользователь Windows |
&НаКлиенте, веб-клиент | прямой записи нет | только через браузер и расширение работы с файлами |
Отсюда три практических правила:
- 📁 Для промежуточных файлов на сервере берите путь от
ПолучитьИмяВременногоФайла("txt")— каталог временных файлов всегда существует и доступен на запись. - 🔐 Если пишете в общую папку из клиент-серверной базы, права на эту папку нужны учётной записи службы сервера 1С, а не пользователю.
- 🌐 Сетевой путь указывайте в формате UNC (
\\fileserver\obmen\), а не через букву сетевого диска: у службы буквы дисков пользователя не подключены.
Перед записью полезно проверить каталог и при необходимости создать его:
&НаСервере
Процедура ПодготовитьКаталог(ПутьКаталога)
Каталог = Новый Файл(ПутьКаталога);
Если Не Каталог.Существует() Тогда
СоздатьКаталог(ПутьКаталога);
КонецЕсли;
КонецПроцедуры
Запись текстового файла: ТекстовыйДокумент и ЗаписьТекста
Для текстовых данных у платформы два основных инструмента. Выбор зависит от объёма.
ТекстовыйДокумент — когда текст небольшой
Объект держит весь текст в памяти и записывает его одним вызовом. Удобно для логов выгрузки, небольших CSV, файлов настроек.
&НаСервере
Процедура ЗаписатьОтчётВФайл(ПутьФайла)
Текст = Новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("Номер;Дата;Сумма");
Выборка = Документы.РеализацияТоваровУслуг.Выбрать();
Пока Выборка.Следующий() Цикл
Строка = СтрШаблон("%1;%2;%3",
Выборка.Номер,
Формат(Выборка.Дата, "ДФ=dd.MM.yyyy"),
Формат(Выборка.СуммаДокумента, "ЧДЦ=2; ЧРД=,; ЧГ=0"));
Текст.ДобавитьСтроку(Строка);
КонецЦикла;
Текст.Записать(ПутьФайла, КодировкаТекста.UTF8);
КонецПроцедуры
Пример рассчитан на конфигурацию, где есть документ РеализацияТоваровУслуг с реквизитом СуммаДокумента, — подставьте свой объект.
💡 Если строку целиком собирает ваш код, проще задать текст сразу: Текст.УстановитьТекст(ИтоговаяСтрока).
ЗаписьТекста — когда строк много
ЗаписьТекста пишет на диск по мере поступления строк и не держит весь файл в памяти. Выгрузка на сотни тысяч строк через ТекстовыйДокумент может съесть сотни мегабайт памяти рабочего процесса, а через ЗаписьТекста — нет.
&НаСервере
Процедура ВыгрузитьКонтрагентов(ПутьФайла)
Запись = Новый ЗаписьТекста(ПутьФайла, КодировкаТекста.UTF8);
Выборка = Справочники.Контрагенты.Выбрать();
Пока Выборка.Следующий() Цикл
Запись.ЗаписатьСтроку(Выборка.Наименование);
КонецЦикла;
Запись.Закрыть();
КонецПроцедуры
Разница между методами:
- ✍️
ЗаписатьСтроку(Текст)— пишет текст и перевод строки. - ✍️
Записать(Текст)— пишет текст без перевода строки, удобно для сборки строки по частям. - 🔒
Закрыть()— сбрасывает буфер на диск и освобождает файл. Без вызова файл остаётся заблокированным, а последние строки могут не попасть на диск.
Всегда вызывайте Закрыть(), в том числе в ветке Исключение. Незакрытый файл держится открытым процессом 1С, и повторная запись завершится ошибкой совместного доступа.
Кодировка, BOM и дописывание в конец файла
Кодировка — второй частый источник жалоб «файл записался кракозябрами».
| Задача | Что указать |
|---|---|
| Обмен с современными системами, JSON, XML | КодировкаТекста.UTF8 |
| Старые программы, ожидающие Windows-1251 | строку "windows-1251" |
| Файл для DOS-утилит, кассового ПО старых версий | КодировкаТекста.OEM |
| Нужен UTF-8 без BOM | запись через двоичные данные (см. ниже) |
КодировкаТекста.ANSI зависит от настроек операционной системы, на которой выполняется код. На сервере под Linux результат будет не тем, что на Windows. Для предсказуемого результата указывайте кодировку явно строкой.
UTF-8 без BOM
При кодировке КодировкаТекста.UTF8 платформа записывает в начало файла метку порядка байтов (BOM). Некоторые веб-сервисы и парсеры её не принимают. Обходной путь — получить двоичные данные из строки с отключённой меткой:
ДанныеФайла = ПолучитьДвоичныеДанныеИзСтроки(ТекстФайла, КодировкаТекста.UTF8, Ложь);
ДанныеФайла.Записать(ПутьФайла);
Третий параметр — признак использования BOM. Функция доступна в платформе 8.3.9 и выше.
Дописывание в существующий файл
Четвёртый параметр конструктора ЗаписьТекста включает режим дописывания. Так ведут собственный лог обработки:
Запись = Новый ЗаписьТекста(ПутьЛога, КодировкаТекста.UTF8, , Истина);
Запись.ЗаписатьСтроку(Формат(ТекущаяДата(), "ДФ='dd.MM.yyyy HH:mm:ss'") + " Выгрузка завершена");
Запись.Закрыть();
Если файла нет, он будет создан. Если есть — новые строки уйдут в конец.
Двоичные данные, ЗаписьДанных и потоки
Картинки, PDF, архивы, файлы из присоединённых файлов — это двоичные данные. Текстовые объекты для них не подходят: они испортят байты при перекодировке.
Готовые двоичные данные
Если данные уже лежат в памяти — например, получены из хранилища значения или HTTP-ответа, — достаточно одного вызова:
ДвоичныеДанныеФайла = ОбъектФайла.ХранилищеФайла.Получить();
ДвоичныеДанныеФайла.Записать(ПутьФайла);
Для HTTP-ответа: Ответ.ПолучитьТелоКакДвоичныеДанные().Записать(ПутьФайла). Если файл большой, при запросе лучше сразу указать имя выходного файла в методе Получить объекта HTTPСоединение — тогда тело ответа не будет целиком загружаться в память.
ЗаписьДанных — побайтовая сборка
Объект ЗаписьДанных (платформа 8.3.9+) пишет в файл или поток числа, байты и строки в нужном порядке байтов. Пригодится, когда формат файла задан спецификацией: заголовок фиксированной длины, затем записи.
Запись = Новый ЗаписьДанных(ПутьФайла);
Запись.ЗаписатьБайт(1); // версия формата
Запись.ЗаписатьЦелое32(КоличествоЗаписей);
Запись.ЗаписатьСтроку("HEADER", КодировкаТекста.UTF8);
Запись.Закрыть();
Файловый поток
Для больших файлов и частичной записи используют поток. Его открывают через менеджер ФайловыеПотоки:
Поток = ФайловыеПотоки.ОткрытьДляЗаписи(ПутьФайла);
Буфер = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ДвоичныеДанныеФайла);
Поток.Записать(Буфер, 0, Буфер.Размер);
Поток.Закрыть();
Поток удобен, когда данные приходят частями: каждый новый кусок записывается вызовом Записать без склейки всего содержимого в памяти. Метод ОткрытьДляЗаписи перезаписывает существующий файл.
| Инструмент | Когда брать | Память |
|---|---|---|
ТекстовыйДокумент | небольшой текст, нужно править строки до записи | весь текст в памяти |
ЗаписьТекста | длинные текстовые выгрузки | построчно |
ДвоичныеДанные.Записать | данные уже в памяти | весь файл в памяти |
ЗаписьДанных | бинарный формат по спецификации | по мере записи |
ФайловыйПоток | большие файлы, запись частями | по мере записи |
Для XML и JSON есть свои писатели: ЗаписьXML и ЗаписьJSON с методом ОткрытьФайл(ПутьФайла). Их логика та же — открыть, записать, закрыть.
Сохранение файла на компьютер пользователя
Типичная задача: на сервере сформировали выгрузку, а пользователь должен выбрать папку и сохранить файл у себя. Прямо с сервера на диск клиента записать нельзя. Схема всегда одна:
- На сервере получить двоичные данные файла .
- Поместить их во временное хранилище:
ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор). - Вернуть адрес на клиент.
- На клиенте вызвать сохранение — платформа покажет диалог выбора места.
&НаКлиенте
Процедура СохранитьВыгрузку(Команда)
Адрес = СформироватьФайлНаСервере();
ПараметрыДиалога = Новый ПараметрыДиалогаПолученияФайлов;
ПараметрыДиалога.Заголовок = "Сохранение выгрузки";
ПолучитьФайлССервераАсинх(Адрес, "vygruzka.csv", ПараметрыДиалога);
КонецПроцедуры
&НаСервере
Функция СформироватьФайлНаСервере()
ИмяВременного = ПолучитьИмяВременногоФайла("csv");
Текст = Новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("Пример;выгрузки");
Текст.Записать(ИмяВременного, КодировкаТекста.UTF8);
Данные = Новый ДвоичныеДанные(ИмяВременного);
УдалитьФайлы(ИмяВременного);
Возврат ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
КонецФункции
ПолучитьФайлССервераАсинх появился в платформе 8.3.18. В более ранних релизах ту же роль выполнял НачатьПолучениеФайлаССервера, а в старом коде встречается синхронный ПолучитьФайл(Адрес, ИмяФайла, Истина).
💡 В типовых конфигурациях на Библиотеке стандартных подсистем удобнее вызвать ФайловаяСистемаКлиент.СохранитьФайл(Оповещение, Адрес, ИмяФайла). Модуль сам учтёт веб-клиент и установку расширения работы с файлами.
Если путь известен заранее и диалог не нужен, на клиенте тонкого клиента
можно записать файл напрямую — тем же ТекстовыйДокумент или ДвоичныеДанные, но асинхронным методом: ЗаписатьАсинх (8.3.18+) или НачатьЗапись в более ранних релизах.
Тонкий, толстый и веб-клиент: что где работает
Поведение одного и того же кода зависит от клиента и настроек конфигурации.
- 🖥️ Толстый клиент (обычное приложение). Синхронная запись на клиенте работает, файловая система доступна напрямую.
- 💻 Тонкий клиент. Доступ к диску пользователя есть. Но если в свойствах конфигурации режим синхронных вызовов расширений платформы запрещён (так в новых типовых), синхронные методы вроде
ТекстовыйДокумент.Записатьна клиенте вызовут ошибку. Используйте асинхронные варианты. - 🌐 Веб-клиент. Без расширения работы с файлами браузер позволяет только скачать файл. С расширением доступны и запись по пути, и диалоги. Подключение проверяют через
НачатьПодключениеРасширенияРаботыСФайлами, установку предлагают черезНачатьУстановкуРасширенияРаботыСФайлами. - ☁️ Сервер. Записывает куда угодно в пределах прав учётной записи. В облачных сервисах и при работе в безопасном режиме доступ к диску сервера закрыт.
Режим синхронных вызовов смотрят в Конфигураторе: корень конфигурации → свойства → раздел «Совместимость».
Безопасный режим и внешние обработки
Внешняя обработка или расширение, подключённые в безопасном режиме, не могут писать файлы на сервере. В типовых конфигурациях на БСП для этого предусмотрены профили безопасности и запрос разрешений на внешние ресурсы. Если обработка в одной базе пишет файл, а в другой падает с ошибкой «Нарушение прав доступа», первым делом проверьте, в каком режиме она подключена.
Ошибки при записи файла и их перехват
Запись на диск — внешняя операция, она может упасть в любой момент: место кончилось, файл открыт в Excel, нет прав. Код должен это переживать.
&НаСервере
Функция ЗаписатьБезопасно(ПутьФайла, ТекстФайла)
Запись = Неопределено;
Попытка
Запись = Новый ЗаписьТекста(ПутьФайла, КодировкаТекста.UTF8);
Запись.Записать(ТекстФайла);
Запись.Закрыть();
Возврат Истина;
Исключение
Если Запись <> Неопределено Тогда
Попытка
Запись.Закрыть();
Исключение
КонецПопытки;
КонецЕсли;
ЗаписьЖурналаРегистрации("Выгрузка в файл",
УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
Возврат Ложь;
КонецПопытки;
КонецФункции
Что здесь сделано:
- Объект создаётся внутри
Попытка— ошибка открытия файла тоже перехватывается. - В ветке
Исключениефайл закрывается отдельной попыткой, чтобы не потерять исходную ошибку. - Подробности уходят в журнал регистрации: так администратор увидит причину, даже если пользователь закрыл сообщение.
Если пишете в файл, который может читать другой процесс (например, обменный каталог), сначала запишите во временный файл рядом, а затем переименуйте через ПереместитьФайл. Так получатель не заберёт недописанный файл.
Частые вопросы
Можно ли записать файл в папку «Мои документы» пользователя?
КаталогДокументов(), в новых релизах есть асинхронный КаталогДокументовАсинх().Как перезаписать существующий файл без вопросов?
ТекстовыйДокумент.Записать, ДвоичныеДанные.Записать и ЗаписьТекста без признака дописывания перезаписывают файл молча. Проверять существование нужно только если перезапись нежелательна.Чем удалить временный файл после записи?
УдалитьФайлы(ПутьФайла). Вызывайте её после закрытия всех объектов, которые держат файл, иначе удаление завершится ошибкой.Почему фоновое задание пишет файл не туда?
Как записать файл в кодировке Windows-1251?
"windows-1251" вторым параметром в ЗаписьТекста или в метод Записать у ТекстовыйДокумент.