Как передать файл на сервер 1С, записать в него данные и вернуть обратно на клиента?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разработка в системе «1С:Предприятие 8.3» на управляемых формах требует четкого понимания клиент-серверной архитектуры. Одной из классических задач, с которой сталкиваются разработчики, является обработка файлов: когда файл находится на стороне клиента, данные для его заполнения генерируются на сервере, а готовый результат должен снова оказаться на компьютере пользователя. Ситуация усложняется, когда сервер работает под управлением ОС Linux, а клиентские рабочие места функционируют на Windows.

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

Почему нельзя просто записать файл по пути с сервера?

Типичная ошибка начинающего разработчика — попытка передать путь к файлу (например, C:\log.txt) с клиента на сервер и выполнить запись напрямую из серверного кода. Рассмотрим, почему это приводит к ошибкам:

  1. Разные физические компьютеры. Сервер 1С и клиентское приложение чаще всего запущены на разных машинах. Сервер не имеет прямого доступа к локальной файловой системе клиентского ПК. Попытка записать данные в C:\log.txt на сервере приведет либо к ошибке отсутствия пути, либо файл запишется на локальный диск самого сервера (если на нем операционная система Windows и запущен соответствующий диск).
  2. Различие операционных систем. Если сервер 1С работает под управлением Linux, а клиент — под Windows, путь вида C:\log.txt является синтаксически некорректным для сервера. В Linux файловая система начинается с корневого каталога / (например, /tmp/), и понятия «диск C:» там просто не существует.
  3. Ограничение прав доступа. Процесс сервера «1С:Предприятия» (обычно под пользователем usr1cv8) работает в изолированном окружении и имеет крайне ограниченные права на запись в каталоги операционной системы.

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

Проанализируем три основных способа реализации этой задачи.


Способ 1. Передача текстовых данных в виде строки (Самый простой вариант)

Если файл гарантированно является текстовым (форматы .txt, .csv, .xml, .json) и имеет сравнительно небольшой объем (до нескольких мегабайт), проще всего отказаться от передачи самого файла. Вместо этого мы считываем весь текст на клиенте в строковую переменную, передаем строку на сервер в качестве параметра процедуры, обрабатываем её и возвращаем измененную строку обратно на клиент, где перезаписываем файл.

Разберем пример реализации этого подхода:


&НаКлиенте
Процедура ОбработатьТекстовыйФайлНаКлиенте()
    
    ПутьКФайлу = "C:\MyFiles\log.txt";
    
    // 1. Читаем файл на клиенте
    ФайлЧтение = Новый ЧтениеТекста(ПутьКФайлу, КодировкаТекста.UTF8);
    ИсходныйТекст = ФайлЧтение.Прочитать();
    ФайлЧтение.Закрыть();
    
    // 2. Вызываем серверный метод для модификации текста
    МодифицированныйТекст = ОбработатьТекстНаСервере(ИсходныйТекст);
    
    // 3. Записываем обновленный текст обратно в файл на клиенте
    ФайлЗапись = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.UTF8);
    ФайлЗапись.ЗаписатьСтроку(МодифицированныйТекст);
    ФайлЗапись.Закрыть();
    
    ПоказатьПредупреждение(, "Файл успешно обработан!");
    
КонецПроцедуры

&НаСервере
Функция ОбработатьТекстНаСервере(Знач ВходнойТекст)
    
    // Выполняем необходимые манипуляции с текстом на сервере
    Результат = ВходнойТекст + Символы.ПС + "Добавлено на сервере 1С в: " + ТекущаяДатаСеанса();
    Возврат Результат;
    
КонецФункции

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

Недостатки: не подходит для бинарных файлов (картинки, архивы, PDF, Excel), а также может приводить к избыточному потреблению оперативной памяти при обработке файлов объемом в десятки и сотни мегабайт.


Способ 2. Использование временного хранилища и Двоичных данных (Универсальный стандарт)

Это самый надежный, отказоустойчивый и архитектурно верный способ в экосистеме 1С. Он работает с любыми типами файлов любой вложенности и объема. Суть метода заключается в преобразовании файла на клиенте в объект ДвоичныеДанные, помещении его во временное хранилище (которое физически находится в базе данных или оперативной памяти сервера приложений), получении его на сервере, обработке и возврате аналогичным путем.

Давайте подробно рассмотрим, как правильно организовать этот процесс с учетом современных стандартов разработки.

Реализация на платформе 1С 8.3 с использованием Двоичных данных

Ниже представлен классический рабочий код, решающий задачу передачи файла на сервер, его модификации и возврата обратно на клиент:


&НаКлиенте
Процедура ВыполнитьОбработкуФайла(Команда)

    ПутьКФайлуКлиент = "C:\log.txt";
    
    // 1. Проверяем существование файла на клиенте
    ФайлНаКлиенте = Новый Файл(ПутьКФайлуКлиент);
    Если Не ФайлНаКлиенте.Существует() Тогда
        ВызватьИсключение "Исходный файл не найден по пути: " + ПутьКФайлуКлиент;
    КонецЕсли;
    
    // 2. Преобразуем файл в двоичные данные
    ДвоичныеДанныеКлиента = Новый ДвоичныеДанные(ПутьКФайлуКлиент);
    
    // 3. Помещаем двоичные данные во временное хранилище
    АдресФайлаВХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеКлиента, ЭтаФорма.УникальныйИдентификатор);
    
    // 4. Передаем управление на сервер для обработки файла
    АдресРезультатаВХранилище = ОбработатьФайлНаСервере(АдресФайлаВХранилище);
    
    // 5. Если сервер вернул адрес, получаем двоичные данные и записываем файл обратно на клиент
    Если ЭтоАдресВременногоХранилища(АдресРезультатаВХранилище) Тогда
        ДвоичныеДанныеРезультата = ПолучитьИзВременногоХранилища(АдресРезультатаВХранилище);
        ДвоичныеДанныеРезультата.Записать(ПутьКФайлуКлиент);
        ПоказатьПредупреждение(, "Данные успешно получены и записаны в файл!");
    Иначе
        ВызватьИсключение "Не удалось получить обработанный файл с сервера.";
    КонецЕсли;

КонецПроцедуры

&НаСервере
Функция ОбработатьФайлНаСервере(Знач АдресВходногоФайла)
    
    // Извлекаем двоичные данные из хранилища
    ДвоичныеДанныеСервер = ПолучитьИзВременногоХранилища(АдресВходногоФайла);
    
    // Создаем временный файл на диске сервера для непосредственной работы утилит
    ИмяВременногоФайлаСервер = ПолучитьИмяВременногоФайла("txt");
    ДвоичныеДанныеСервер.Записать(ИмяВременногоФайлаСервер);
    
    // --- НАЧАЛО БЛОКА ОБРАБОТКИ НА СЕРВЕРЕ ---
    // Здесь мы можем изменять файл. Например, допишем в него строку.
    ЗаписьТекстаСервер = Новый ЗаписьТекста(ИмяВременногоФайлаСервер, КодировкаТекста.UTF8,,, Истина); // Дозапись
    ЗаписьТекстаСервер.ЗаписатьСтроку("Новая строка, добавленная сервером приложений.");
    ЗаписьТекстаСервер.Закрыть();
    // --- КОНЕЦ БЛОКА ОБРАБОТКИ НА СЕРВЕРЕ ---
    
    // Читаем измененный файл обратно в двоичные данные
    ДвоичныеДанныеРезультат = Новый ДвоичныеДанные(ИмяВременногоФайлаСервер);
    
    // Помещаем результат во временное хранилище
    АдресРезультата = ПоместитьВоВременноеХранилище(ДвоичныеДанныеResult, ЭтаФорма.УникальныйИдентификатор);
    
    // Обязательно очищаем за собой временные файлы на физическом диске сервера!
    Попытка
        УдалитьФайлы(ИмяВременногоФайлаСервер);
    Исключение
        ЗаписьЖурналаРегистрации("ОбработкаФайла", УровеньЖурналаРегистрации.Предупреждение,, 
            "Не удалось удалить временный файл сервера: " + ОписаниеОшибки());
    КонецПопытки;
    
    Возврат АдресРезультата;

КонецФункции

Важное архитектурное замечание об очистке диска сервера: Функция ПолучитьИмяВременногоФайла() резервирует уникальное имя и создает физический файл в системной директории сервера (например, /tmp/ в Linux или C:\Users\...\Temp в Windows). Платформа 1С автоматически удаляет данные из оперативной памяти временного хранилища при закрытии формы, но не удаляет созданные физические файлы на жестком диске сервера. Если пренебрегать вызовом УдалитьФайлы() в блоке Попытка...Исключение, жесткий диск сервера со временем полностью переполнится временным мусором, что приведет к аварийной остановке операционной системы.


Способ 3. Современные асинхронные методы (Для версий платформы 8.3.15 и выше)

Начиная с версии платформы 8.3.15, фирма «1С» изменила концепцию работы с файловой системой на клиенте с целью отказа от модальных окон и поддержки работы в веб-клиенте без установки расширения работы с файлами. На смену устаревшим методам ПолучитьФайлы() и ПоместитьФайл() пришли современные асинхронные конструкции с использованием ключевых слов Асинх и Ждать.

Рассмотрим, как выглядит современный асинхронный перенос файла на сервер и обратно без блокировки интерфейса пользователя:


&НаКлиенте
Асинх Процедура ВыполнитьАсинхроннуюОбработкуФайла(Команда)

    ПутьКФайлуКлиент = "C:\log.txt";
    
    // Помещаем файл на сервер асинхронно
    РезультатПомещения = Ждать НачатьПомещениеФайлаНаСерверАсинх(,,, ПутьКФайлуКлиент, УникальныйИдентификатор);
    
    Если РезультатПомещения = Неопределено Тогда
        Возврат; // Пользователь отказался от передачи файла или произошла ошибка
    КонецЕсли;
    
    // Передаем полученный адрес на сервер для обработки
    АдресОбработанногоФайла = ОбработатьФайлНаСервере(РезультатПомещения.Адрес);
    
    // Получаем файл обратно с сервера на клиент в асинхронном режиме
    ПараметрыПолучения = Новый ПараметрыПолученияФайлаССервера;
    ПараметрыПолучения.Интерактивно = Ложь; // Без вывода диалогового окна выбора пути
    
    Ждать НачатьПолучениеФайлаССервераАсинх(АдресОбработанногоФайла, ПутьКФайлуКлиент, ПараметрыПолучения);
    
    ПоказатьПредупреждение(, "Файл асинхронно обработан и сохранен обратно на диск!");

КонецПроцедуры

Использование неблокирующих асинхронных вызовов гарантирует, что сеанс пользователя и интерфейс программы не «зависнут» в режиме ожидания, даже если по сети передается объемный файл или сервер выполняет длительные расчеты.


Специфическая проблема: выполнение консольных команд и перенаправление вывода (Случай с MySQL)

В Сообщении 12 автор столкнулся со следующей проблемой: на сервере запускается внешняя консольная утилита с перенаправлением вывода в текстовый файл, однако создаваемый файл оказывается пустым:


ТестСоед = "mysql -u user -p pass -h host EXPLAIN test_tab; >"+ ИмяФайла;
ЗапуститьПриложение(ТестСоед);

Выясним причину этой ошибки и разберем пути её решения.

Почему файл остается пустым?

  1. Непосредственный запуск утилиты. Функция ЗапуститьПриложение() запускает указанный исполняемый файл напрямую. Символ перенаправления потока вывода > (или >>) не является параметром программы mysql. Он обрабатывается исключительно командным интерпретатором операционной системы (командной строкой shell). Платформа 1С запускает утилиту mysql напрямую, передавая ей символ > как обычный аргумент, из-за чего утилита завершается с синтаксической ошибкой, так и не выполнив запрос.
  2. Отсутствие ожидания завершения. Если запуск утилиты выполняется асинхронно (третий параметр метода ЗапуститьПриложение равен Ложь по умолчанию), платформа 1С, не дожидаясь окончания работы внешней программы и наполнения файла данными, мгновенно считывает еще пустой файл в объект ДвоичныеДанные и отправляет его клиенту.

Решение для Linux-сервера

Для корректного перенаправления вывода в файл под управлением операционной системы Linux команду необходимо запускать внутри интерпретатора командной строки (shell), используя параметр -c. Также критически важно установить флаг ожидания завершения программы в значение Истина:


&НаСервере
Функция ВыполнитьЗапросКБДНаСервере()
    
    ИмяФайла = ПолучитьИмяВременногоФайла("txt");
    КодВозврата = Неопределено;
    
    // Формируем вызов через командную оболочку Linux (/bin/sh)
    КомандаЗапуска = "/bin/sh -c ""mysql -u user -p pass -h host -e 'EXPLAIN test_tab;' > " + ИмяФайла + """";
    
    // Запускаем приложение с ОЖИДАНИЕМ завершения (3-й параметр = Истина)
    ЗапуститьПриложение(КомандаЗапуска, , Истина, КодВозврата);
    
    Если КодВозврата <> 0 Тогда
        ВызватьИсключение "Ошибка выполнения консольной команды. Код возврата: " + КодВозврата;
    КонецЕсли;
    
    // Читаем наполненный файл в двоичные данные
    ДвоичныеДанные = Новый ДвоичныеДанные(ИмяФайла);
    Адрес = ПоместитьВоВременноеХранилище(ДвоичныеДанные);
    
    УдалитьФайлы(ИмяФайла);
    
    Возврат Адрес;
    
КонецФункции

Различие кодировок и символов перевода строки (Linux vs Windows)

При обмене текстовыми файлами между сервером на базе Linux и клиентом на ОС Windows разработчики регулярно сталкиваются с проблемой искажения спецсимволов и некорректного отображения кириллицы.

Кодировка текста. В семействе ОС Linux стандартом де-факто является кодировка UTF-8. На операционных системах Windows для простых файлов с расширением .txt исторически используется кодировка ANSI (в русской локализации — Windows-1251). Если текстовый файл был записан на сервере Linux в UTF-8 без BOM-маркера, клиентское приложение в Windows при открытии "блокнотом" может отобразить текст некорректно («кракозябры»). Рекомендуется всегда явно указывать кодировку КодировкаТекста.UTF8 при создании объектов чтения/записи файлов на обеих сторонах.

Символы перевода строки. Разные операционные системы используют разные управляющие символы для перехода на новую строку:

Если вы формируете скрипт или текстовый файл на Windows-клиенте для последующего исполнения на Linux-сервере, скрытые символы \r могут вызвать критические ошибки командных интерпретаторов (bash будет выдавать ошибку разметки строк). Для исправления таких файлов непосредственно на сервере Linux перед запуском утилит целесообразно использовать утилиту конвертации dos2unix.


Вопросы безопасности: защита от Command Injection

Когда вы динамически собираете строку для запуска внешней утилиты через ЗапуститьПриложение(), подставляя в нее параметры, полученные из полей ввода или базы данных, вы открываете критическую уязвимость безопасности — внедрение команд (Command Injection).

Если злонамеренный пользователь передаст в параметр, например, имя хоста или имя таблицы, содержащие спецсимволы (такие как ;, &, |, `), командный интерпретатор Linux выполнит его команду сразу после основной команды. Например, если вместо имени таблицы передать строку: test_tab; rm -rf /, сервер 1С попытается выполнить деструктивную команду с правами системной учетной записи 1С.

Рекомендации по защите:

  1. Никогда не доверяйте внешним входным данным без предварительной валидации.
  2. Используйте регулярные выражения (объект РегулярныеВыражения) для жесткой фильтрации параметров, разрешая только буквы латиницы, цифры и знаки подчеркивания.
  3. По возможности передавайте параметры команд через промежуточные файлы конфигурации или переменные окружения, избегая прямой конкатенации строк запуска командной строки.

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

← На главную