Запись файлов из кода 1С 8.3: сервер, клиент, кодировки и потоки
Содержание
Чтобы записать файл в 1С программно, соберите содержимое в объект и вызовите его метод Записать(ИмяФайла). Для текста подходит ТекстовыйДокумент или ЗаписьТекста, для картинок, архивов и PDF — ДвоичныеДанные, для больших объёмов — ФайловыйПоток вместе с ЗаписьДанных. Код работает в платформе 8.3 одинаково во всех типовых конфигурациях: это механизм платформы, а не БП или ЗУП.
Главная ловушка не в синтаксисе. Файл, записанный в серверной процедуре, окажется на диске сервера 1С, а не на компьютере пользователя. Разберём, как выбрать место записи, как не испортить кодировку и как доставить файл пользователю в тонком и веб-клиенте.
Где окажется файл: сервер или компьютер пользователя
В клиент-серверном варианте у кода два «диска». Процедура с директивой &НаСервере видит файловую систему сервера 1С. Процедура &НаКлиенте видит диск компьютера, где запущен тонкий клиент.
| Где выполняется код | Чей диск | От чьего имени пишет |
|---|---|---|
&НаСервере, фоновое или регламентное задание | сервер 1С | учётная запись службы агента сервера (часто USR1CV8) |
&НаКлиенте, тонкий клиент | компьютер пользователя | пользователь Windows |
&НаКлиенте, веб-клиент | нет прямого доступа | только через расширение работы с файлами |
| Файловая база | один компьютер | пользователь Windows |
Отсюда три практических правила:
- 📁 Путь
C:\Выгрузка\в серверном коде — это папка на сервере. Пользователь её не увидит. - 🔐 Для записи в сетевую папку из серверного кода права на неё нужны учётной записи службы 1С, а не бухгалтеру.
- ⏱ Регламентные задания пишут только на сервере. Клиента в этот момент может не быть вообще.
Для промежуточных файлов на сервере берите имя через ПолучитьИмяВременногоФайла("txt"). Платформа сама выберет каталог временных файлов, а после работы файл нужно удалить через УдалитьФайлы(ИмяФайла).
Как записать текстовый файл в 1С
ТекстовыйДокумент — когда текст небольшой
Самый короткий способ. Строки накапливаются в памяти, затем документ записывается целиком.
&НаСервере
Процедура ЗаписатьТекстНаСервере(ИмяФайла)
Текст = Новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("Номер;Дата;Сумма");
Текст.ДобавитьСтроку("15;01.03;1250,00");
Текст.ДобавитьСтроку("16;02.03;980,50");
Текст.Записать(ИмяФайла, КодировкаТекста.UTF8);
КонецПроцедуры
Второй параметр метода Записать задаёт кодировку. Без него платформа запишет файл в кодировке по умолчанию, и сторонняя программа может показать вместо кириллицы «кракозябры».
ЗаписьТекста — когда строк много
ТекстовыйДокумент держит всё содержимое в памяти. Для выгрузки в сотни тысяч строк удобнее ЗаписьТекста: она сразу пишет на диск.
&НаСервере
Процедура ВыгрузитьНоменклатуру(ИмяФайла)
Запись = Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8);
Запрос = Новый Запрос("ВЫБРАТЬ Код, Наименование ИЗ Справочник.Номенклатура");
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
Запись.ЗаписатьСтроку(СтрШаблон("%1;%2", Выборка.Код, Выборка.Наименование));
КонецЦикла;
Запись.Закрыть();
КонецПроцедуры
Конструктор принимает ещё и признак дописывания. Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8, , Истина) добавит строки в конец существующего файла, а не перезапишет его. Так ведут простые журналы обмена.
Всегда вызывайте Закрыть(). Пока объект открыт, файл заблокирован: другая программа не сможет его прочитать, а часть данных может остаться в буфере и не попасть на диск.
UTF-8 без BOM
КодировкаТекста.UTF8 пишет в начало файла метку BOM — три служебных байта. Excel и 1С её любят, а многие сайты, банки и API считают её мусором в первой строке. Надёжный способ записать файл без BOM — собрать двоичные данные из строки с явным отказом от метки:
Содержимое = "Номер;Дата;Сумма" + Символы.ПС + "15;01.03;1250,00";
Данные = ПолучитьДвоичныеДанныеИзСтроки(Содержимое, КодировкаТекста.UTF8, Ложь);
Данные.Записать(ИмяФайла);
Третий параметр Ложь и отключает BOM. Функция доступна в платформе с номером не ниже 8.3.9.
Какую кодировку выбрать
| Кто будет читать файл | Кодировка | Как указать |
|---|---|---|
| Excel, открытие CSV двойным щелчком | UTF-8 с BOM или ANSI | КодировкаТекста.UTF8 или КодировкаТекста.ANSI |
| Сайт, API, Linux-скрипт | UTF-8 без BOM | ПолучитьДвоичныеДанныеИзСтроки(..., КодировкаТекста.UTF8, Ложь) |
| Старая DOS-программа, некоторые банк-клиенты | OEM (cp866) | КодировкаТекста.OEM |
| Другая база 1С | UTF-8 | КодировкаТекста.UTF8 |
Если формат задаёт получатель, кодировку берите из его спецификации, а не по таблице. Банки и операторы обычно прямо пишут её в описании формата обмена.
XML, JSON и табличный документ
Для структурированных форматов писать текст вручную не нужно:
- 🧾
ЗаписьXML— методомОткрытьФайл(ИмяФайла, "UTF-8"), затемЗаписатьНачалоЭлемента,ЗаписатьТекст,ЗаписатьКонецЭлемента,Закрыть(). - 🧩
ЗаписьJSON— тожеОткрытьФайл, затемЗаписатьJSON(Запись, Структура)иЗакрыть(). - 📊
ТабличныйДокумент.Записать(ИмяФайла, ТипФайлаТабличногоДокумента.XLSX)— готовый отчёт сразу в Excel. Есть и другие типы: PDF, MXL, HTML.
Двоичные данные и файловые потоки
Записать двоичные данные целиком
Картинка из реквизита, файл из присоединённых файлов, ответ HTTP-сервиса — всё это ДвоичныеДанные. Сохраняются они одной строкой:
&НаСервере
Процедура СохранитьКартинку(Ссылка, ИмяФайла)
Данные = Ссылка.ХранилищеКартинки.Получить(); // реквизит типа ХранилищеЗначения
Если ТипЗнч(Данные) = Тип("ДвоичныеДанные") Тогда
Данные.Записать(ИмяФайла);
КонецЕсли;
КонецПроцедуры
Проверка типа нужна потому, что пустое хранилище вернёт Неопределено, и вызов Записать упадёт с ошибкой.
Потоки для больших файлов
ДвоичныеДанные целиком лежат в памяти. Для файла на несколько сотен мегабайт это плохая идея: сеанс съест память рабочего процесса. С платформы 8.3.9 есть потоковая модель: ФайловыйПоток открывает файл, а ЗаписьДанных пишет в него частями.
&НаСервере
Процедура ЗаписатьПотоком(ИмяФайла, МассивСтрок)
Поток = Новый ФайловыйПоток(ИмяФайла, РежимОткрытияФайла.Создать, ДоступКФайлу.Запись);
Запись = Новый ЗаписьДанных(Поток, КодировкаТекста.UTF8);
Для Каждого Строка Из МассивСтрок Цикл
Запись.ЗаписатьСтроку(Строка);
КонецЦикла;
Запись.Закрыть();
Поток.Закрыть();
КонецПроцедуры
ЗаписьДанных умеет писать не только строки, но и отдельные байты, числа и целые блоки двоичных данных методом Записать. Это удобно, когда формат файла задан побайтно, например при выгрузке для банковского или кассового ПО.
Какой объект выбрать
| Объект | Когда брать | Риск |
|---|---|---|
ТекстовыйДокумент | короткий текст, лог, CSV до десятков тысяч строк | весь текст в памяти |
ЗаписьТекста | большая текстовая выгрузка, дописывание в конец | забыли Закрыть() — файл заблокирован |
ДвоичныеДанные.Записать | готовый файл из базы или из HTTP-ответа | большой файл занимает память |
ФайловыйПоток + ЗаписьДанных | файлы от сотен мегабайт, побайтовые форматы | больше кода, два объекта закрывать |
Сохранение файла на компьютер пользователя
Пользователь нажимает кнопку «Выгрузить» и ждёт файл у себя. Схема всегда одна: сформировать данные на сервере, передать их через временное хранилище, принять на клиенте.
Шаг 1. Данные на сервере кладём во временное хранилище
&НаСервере
Функция ПодготовитьФайлНаСервере()
Текст = Новый ТекстовыйДокумент;
Текст.ДобавитьСтроку("Выгрузка от " + Формат(ТекущаяДатаСеанса(), "ДФ=dd.MM.yyyy"));
ИмяВременного = ПолучитьИмяВременногоФайла("txt");
Текст.Записать(ИмяВременного, КодировкаТекста.UTF8);
Адрес = ПоместитьВоВременноеХранилище(Новый ДвоичныеДанные(ИмяВременного), УникальныйИдентификатор);
УдалитьФайлы(ИмяВременного);
Возврат Адрес;
КонецФункции
УникальныйИдентификатор формы привязывает данные к её жизни: закрыли форму — хранилище очистилось.
Шаг 2. Принимаем файл на клиенте
В конфигурациях на БСП (БП 3.0, ЗУП 3.1, УТ 11) проще всего вызвать готовую процедуру библиотеки:
&НаКлиенте
Процедура Выгрузить(Команда)
Адрес = ПодготовитьФайлНаСервере();
ФайловаяСистемаКлиент.СохранитьФайл(Неопределено, Адрес, "Выгрузка.txt");
КонецПроцедуры
Она сама покажет диалог сохранения, подключит расширение в веб-клиенте и учтёт режим модальности.
В самописной конфигурации без БСП используйте асинхронный метод платформы:
&НаКлиенте
Процедура Выгрузить(Команда)
Адрес = ПодготовитьФайлНаСервере();
Файлы = Новый Массив;
Файлы.Добавить(Новый ОписаниеПередаваемогоФайла("Выгрузка.txt", Адрес));
НачатьПолучениеФайлов(Новый ОписаниеОповещения("ПослеСохранения", ЭтотОбъект), Файлы, , Истина);
КонецПроцедуры
&НаКлиенте
Процедура ПослеСохранения(ПолученныеФайлы, ДополнительныеПараметры) Экспорт
Если ПолученныеФайлы = Неопределено Тогда
Возврат; // пользователь отменил диалог
КонецЕсли;
Сообщить("Файл сохранён");
КонецПроцедуры
Последний параметр Истина означает интерактивный режим: пользователь сам выберет папку в диалоге.
Старый ПолучитьФайл(Адрес, ИмяФайла) — модальный. В конфигурации с запретом модальности (так настроены все современные типовые) он вызовет ошибку «Использование модальных окон в данном режиме запрещено». Берите асинхронные варианты.
Выбор пути через диалог
Если файл надо писать прямо на клиенте, путь спрашивают через ДиалогВыбораФайла в режиме РежимДиалогаВыбораФайла.Сохранение. Показывают его тоже асинхронно — методом Показать(ОписаниеОповещения). В процедуру оповещения придёт массив выбранных путей или Неопределено, если пользователь нажал «Отмена».
Особенности тонкого, толстого и веб-клиента
- 🖥 Тонкий клиент. Код
&НаКлиентеможет писать на диск пользователя напрямую:ТекстовыйДокумент.Записать,ДвоичныеДанные.Записатьработают. Но доступ ограничен правами пользователя Windows: папкиProgram Filesи корень дискаC:\часто закрыты на запись. - 🌐 Веб-клиент. Браузер не пускает страницу на диск. Без расширения работы с файлами пользователь получит только стандартное скачивание в папку загрузок. Для записи по пути нужно подключить расширение
:
НачатьПодключениеРасширенияРаботыСФайлами, а при отказе — предложить установку черезНачатьУстановкуРасширенияРаботыСФайлами. - 🧱 Толстый клиент, обычные формы. Код выполняется локально, и многие ограничения тонкого клиента не действуют. Но в клиент-серверной базе серверные процедуры общих модулей всё равно пишут на сервер.
- 🔒 Внешние обработки и расширения в безопасном режиме. Доступ к файловой системе запрещён, пока администратор не разрешит его в профиле безопасности или не снимет безопасный режим при подключении. Ошибка звучит как «Нарушение прав доступа» при вызове
Записать.
Проверить, где выполняется код, можно через инструкции препроцессора #Если Сервер Тогда и #Если ВебКлиент Тогда. Это удобно в общих модулях, которые вызываются из разных мест.
Ошибки при записи и как их перехватить
Любая файловая операция может упасть: нет папки, нет прав, файл открыт в Excel, диск заполнен. Оборачивайте запись в Попытка:
&НаСервере
Функция ЗаписатьБезопасно(Данные, ИмяФайла)
Каталог = Новый Файл(ИмяФайла).Путь;
Если Не Новый Файл(Каталог).Существует() Тогда
СоздатьКаталог(Каталог);
КонецЕсли;
Попытка
Данные.Записать(ИмяФайла);
Исключение
ЗаписьЖурналаРегистрации("Выгрузка файла", УровеньЖурналаРегистрации.Ошибка, , ,
ОписаниеОшибки());
Возврат Ложь;
КонецПопытки;
Возврат Истина;
КонецФункции
Текст из ОписаниеОшибки() попадёт в журнал регистрации: его найдёт администратор, даже если пользователь закрыл окно с сообщением.
Недопустимые символы в имени файла
Имя файла часто собирают из данных: наименование контрагента, номер документа, дата. В них легко попадают символы, которые Windows запрещает в именах: \ / : * ? " < > |. Например, ООО "Ромашка" или дата со слешами 01/03. Запись упадёт с ошибкой создания файла, хотя папка есть и права в порядке.
Перед записью замените запрещённые символы, например на подчёркивание:
Запрещенные = "\/:*?""<>|";
Имя = Наименование;
Для Номер = 1 По СтрДлина(Запрещенные) Цикл
Имя = СтрЗаменить(Имя, Сред(Запрещенные, Номер, 1), "_");
КонецЦикла;
Двойная кавычка внутри строкового литерала 1С записывается двумя кавычками подряд, поэтому в строке Запрещенные девять символов. Дату в имя вставляйте через Формат(Дата, "ДФ=yyyyMMdd") — без точек и слешей.
Ещё одна неочевидная причина — антивирус на сервере, который на доли секунды блокирует только что созданный файл. Если запись идёт в цикле и падает через раз, добавьте повтор с паузой или пишите сначала во временный файл, а потом перемещайте его через ПереместитьФайл.
Частые вопросы
Как записать файл в кодировке Windows-1251?
КодировкаТекста.ANSI при записи. На сервере с русскими региональными настройками это и есть Windows-1251. Для однозначности можно передать строку "windows-1251".Можно ли дописать строку в конец существующего файла?
ЗаписьТекста с четвёртым параметром Истина: Новый ЗаписьТекста(ИмяФайла, КодировкаТекста.UTF8, , Истина). Новые строки встанут в конец, старое содержимое останется.Как сохранить файл из регламентного задания в сетевую папку?
\\server\share\file.txt, а не букву диска: сетевые диски пользователя служба 1С не видит. Права на папку выдайте учётной записи, под которой работает агент сервера.Чем удалить временный файл после записи?
УдалитьФайлы(ИмяФайла). Вызывайте её после того, как все объекты записи закрыты, иначе удаление упадёт с ошибкой доступа.Как записать файл в каталог пользователя без диалога?
НачатьПолучениеКаталогаДокументов, затем записать файл по полному пути. В веб-клиенте для этого нужно подключённое расширение работы с файлами.