Как программно сохранить текстовый файл при работе через веб-сервер 1С на локальный компьютер пользователя?

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

В работе с платформой 1С:Предприятие 8, особенно при использовании веб-клиента и облачных баз, возникает частая задача: программное сохранение текстового файла на локальный компьютер пользователя. На первый взгляд задача кажется простой, ведь в файловой базе или при работе в тонком клиенте мы можем легко использовать метод ТекстовыйДокумент.Записать(), указав прямой путь к файлу на диске. Однако, когда речь заходит о работе через веб-сервер, этот подход перестает работать, и мы сталкиваемся с ошибками, как и наш коллега в исходном сообщении.

Почему прямой способ сохранения файла не работает в веб-клиенте?

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

  1. Разделение контекстов выполнения. При работе через веб-сервер код выполняется в двух различных контекстах: на стороне сервера (где установлена база 1С и веб-сервер) и на стороне клиента (в веб-браузере пользователя). Метод ТекстовыйДокумент.Записать(ПутьКФайлу, ...) пытается сохранить файл по указанному пути ПутьКФайлу на той машине, где выполняется этот код. Если код выполняется на сервере (например, в функции &НаСервере), он ищет путь на диске сервера. Если же этот путь указывает на локальный диск пользователя (например, C:\МояПапка\файл.txt), серверная машина ничего не знает о таком пути. У нее может быть другая операционная система (например, Linux, как упоминается в одном из сообщений), и диск C для нее просто не существует.
  2. Ограничения безопасности веб-браузеров. Современные веб-браузеры имеют строгие меры безопасности, которые запрещают веб-страницам (в том числе веб-клиенту 1С) прямой доступ к файловой системе компьютера пользователя без его явного разрешения. Это сделано для предотвращения вредоносных действий, таких как запись, чтение или удаление файлов без ведома пользователя.
  3. Пример ошибки. В исходном коде, предоставленном автором: ФайлTXT.Записать( Объект.СсылкаНаФайл + "\НоменклатураОстаткиИЦены.txt", ...), переменная Объект.СсылкаНаФайл, вероятно, содержит путь к файлу на локальном ПК пользователя. Когда этот код выполняется на сервере, сервер пытается записать файл по пути, который для него не существует или является некорректным. В итоге мы получаем ошибку, потому что сервер не может найти указанный путь на своей файловой системе.

Правильный подход: использование временного хранилища 1С

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

  1. Шаг 1: Формирование и временное сохранение файла на сервере.

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

    1. Создаем временный файл на сервере. Используем функцию ПолучитьИмяВременногоФайла(), чтобы получить уникальное имя для временного файла на диске сервера. Эта функция генерирует имя файла в системной временной папке, что гарантирует, что мы не перезапишем существующие файлы и что файл будет создан в месте, доступном для серверного процесса.
    2. Записываем данные во временный файл. Создаем экземпляр ТекстовыйДокумент, устанавливаем в него наши текстовые данные (например, из переменной ТекстовыеДанные) и записываем этот документ в полученный временный файл на диске сервера. Важно указать кодировку текста, например, КодировкаТекста.UTF8, чтобы избежать проблем с символами при дальнейшем использовании файла.
    3. Преобразуем файл в двоичные данные. После записи файла на сервер, нам необходимо представить его содержимое в виде ДвоичныеДанные. Это универсальный формат для передачи файлов внутри системы 1С, который может быть легко сериализован и передан между контекстами.
    4. Помещаем двоичные данные во временное хранилище. Полученные ДвоичныеДанные помещаются во ВременноеХранилище 1С с помощью функции ПоместитьВоВременноеХранилище(). Эта функция возвращает уникальный адрес (строку), по которому эти данные будут доступны в течение определенного времени. Этот адрес — ключ, который мы передадим клиенту. Мы можем использовать любой уникальный идентификатор или просто Неопределено в качестве второго параметра, чтобы 1С сама сгенерировала его.
    5. Удаляем временный файл на сервере. После успешного помещения данных во временное хранилище, оригинальный временный файл на диске сервера становится ненужным и должен быть удален, чтобы не засорять файловую систему сервера и освободить ресурсы. Используем УдалитьФайлы().
  2. Шаг 2: Передача адреса на клиент и сохранение файла.

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

    1. Получаем адрес временного хранилища на клиенте. Серверная функция, выполняющая Шаг 1, должна вернуть полученный адрес временного хранилища на клиент. Это строка, которую мы передаем из серверного контекста в клиентский.
    2. Используем клиентский метод ПолучитьФайл(). На клиенте вызываем глобальный метод ПолучитьФайл(), передав ему адрес из временного хранилища в качестве первого параметра. Вторым параметром указываем предлагаемое имя файла для сохранения (например, "НоменклатураОстаткиИЦены.txt"), а третьим (необязательным) — Истина для включения интерактивного режима, который вызовет стандартный диалог сохранения файла в браузере. Без третьего параметра файл может быть автоматически сохранен в папку загрузок браузера с именем по умолчанию.
    3. Альтернатива для современных платформ: асинхронные методы. Для платформы 8.3.15 и выше рекомендуется использовать асинхронные методы, такие как НачатьПолучениеФайлаССервера(). Эти методы более оптимальны для веб-клиента, не требуют установки расширений для работы с файлами и позволяют работать с файлами больших размеров без зависания интерфейса. Они принимают ОписаниеОповещения, которое позволяет обработать результат сохранения файла после его завершения.

Примеры кода для реализации решения

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

Серверный код: Формирование и помещение файла во временное хранилище


&НаСервере
// Функция для формирования текстовых данных, создания временного файла
// на сервере, помещения его во временное хранилище и возврата адреса.
// Параметры:
//   ТекстовыеДанные - Строка, содержимое текстового файла.
//   УникальныйИдентификатор - УникальныйИдентификатор или Строка для идентификации данных в хранилище.
// Возвращаемое значение:
//   Строка - адрес файла во временном хранилище.
Функция СформироватьИПоместитьФайлНаСервере(ТекстовыеДанные, УникальныйИдентификатор = Неопределено)
    Перем ИмяВременногоФайлаНаСервере;
    Попытка
        // 1. Получаем уникальное имя для временного файла на сервере.
        ИмяВременногоФайлаНаСервере = ПолучитьИмяВременногоФайла("txt");
        
        // Создаем новый текстовый документ и устанавливаем в него переданные данные.
        ТекстовыйДокументСервера = Новый ТекстовыйДокумент;
        ТекстовыйДокументСервера.УстановитьТекст(ТекстовыеДанные);
        
        // Записываем текстовый документ во временный файл на сервере.
        // Используем кодировку UTF8 для универсальности и совместимости.
        ТекстовыйДокументСервера.Записать(ИмяВременногоФайлаНаСервере, КодировкаТекста.UTF8);

        // 2. Получаем двоичные данные из только что созданного файла.
        ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ИмяВременногоФайлаНаСервере);

        // 3. Помещаем двоичные данные во временное хранилище и получаем его адрес.
        // Если УникальныйИдентификатор не передан, система сгенерирует его сама.
        АдресВХранилище = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла, УникальныйИдентификатор);

        Возврат АдресВХранилище;
    Исключение
        // В случае ошибки выводим сообщение и возвращаем Неопределено.
        Сообщить("Ошибка при формировании и помещении файла на сервере: " + ОписаниеОшибки(), СтатусСообщения.Важное);
        Возврат Неопределено;
    Наконец
        // 4. Важный шаг: Удаляем временный файл на сервере, чтобы избежать накопления мусора.
        Если ЗначениеЗаполнено(ИмяВременногоФайлаНаСервере) Тогда
            Попытка
                УдалитьФайлы(ИмяВременногоФайлаНаСервере);
            Исключение
                // Игнорируем ошибку удаления, так как файл уже передан во временное хранилище.
                // При необходимости можно добавить логирование этой ситуации.
            КонецПопытки;
        КонецЕсли;
    КонецПопытки;
КонецФункции

Клиентский код: Получение файла с сервера и сохранение на локальный диск


&НаКлиенте
// Процедура для выгрузки файла на клиент.
// Вызывается, например, по нажатию кнопки на форме.
Процедура ВыгрузитьФайлНаКлиент(Команда)
    // Пример текстовых данных. В реальной системе это могут быть
    // данные, полученные из отчета, справочника, регистра, сформированные на сервере и т.д.
    ТекстовыеДанныеДляФайла = "Это тестовые данные для файла, сгенерированные " + ТекущаяДата() + ". " +
                              "Пример списка номенклатуры, остатков и цен.";

    // 1. Вызываем серверную функцию для формирования файла, помещения его
    // во временное хранилище и получения адреса этого хранилища.
    АдресФайлаВХранилище = СформироватьИПоместитьФайлНаСервере(ТекстовыеДанныеДляФайла);

    Если ЗначениеЗаполнено(АдресФайлаВХранилище) Тогда
        // 2. Вызываем клиентский метод для сохранения файла.
        //    ИмяФайлаДляСохранения - это предлагаемое имя файла для сохранения на клиенте.
        //    Истина - означает интерактивный режим, при котором пользователю
        //    будет предложено выбрать место сохранения с помощью диалога браузера.
        ИмяФайлаДляСохранения = "НоменклатураОстаткиИЦены.txt";
        
        // Для платформы 8.3.15 и выше рекомендуется использовать асинхронный метод
        // НачатьПолучениеФайлаССервера(). Он работает более корректно в веб-клиенте
        // и не требует установки расширения для работы с файлами.
        // НачатьПолучениеФайлаССервера(Новый ОписаниеОповещения("ПослеПолученияФайла", ЭтаФорма), 
        //                               АдресФайлаВХранилище, ИмяФайлаДляСохранения, Истина);

        // Если вы используете более старую платформу или расширение для работы с файлами установлено:
        ПолучитьФайл(АдресФайлаВХранилище, ИмяФайлаДляСохранения, Истина);

        // Сообщить("Файл успешно передан на клиент. Проверьте папку загрузок или указанное место сохранения.");
    Иначе
        Сообщить("Не удалось получить файл из временного хранилища на сервере.", СтатусСообщения.Важное);
    КонецЕсли;
КонецПроцедуры

// Обработчик оповещения для НачатьПолучениеФайлаССервера (если используется асинхронный вызов)
// &НаКлиенте
// Процедура ПослеПолученияФайла(Результат, ДополнительныеПараметры) Экспорт
//     Если Результат = Истина Тогда
//         Сообщить("Файл успешно сохранен на клиенте.");
//     Иначе
//         Сообщить("Сохранение файла отменено пользователем или произошла ошибка при получении.", СтатусСообщения.Важное);
//     КонецЕсли;
// КонецПроцедуры

Разбор типичных ошибок при работе с файлами в веб-клиенте

Разберем распространенную ошибку, которую допустил автор темы в сообщении 18, и выясним причину ее возникновения. Это поможет нам лучше понять логику работы с файлами в веб-клиенте.

Оригинальный код из сообщения:


&НаКлиенте
Процедура ВыгрузитьДанные(Команда)
    // ... получение текстовых данных ...
    ФайлTXT = Новый ТекстовыйДокумент;
    ФайлTXT.УстановитьТекст(СтруктураДан.ТекстовыеДанные);
    // ...
    Адрес = ВыгрузкаДанныхЛокально(ФайлTXT); // Здесь переменная "Адрес" получает локальный путь
    ПолучитьФайл(Адрес, "C:\Users\User\Desktop\Apple\тестирование\from_server.txt"); 
КонецПроцедуры

Функция ВыгрузкаДанныхЛокально(Данные)
    ИмяПромежуточногоФайла = ПолучитьИмяВременногоФайла("txt");
    Данные.Записать(ИмяПромежуточногоФайла); // Файл записывается на клиентский диск в temp-папку
    Возврат ИмяПромежуточногоФайла; // Возвращается локальный путь к файлу (например, C:\...)
КонецФункции

Причина ошибки: Автор пытался использовать метод ПолучитьФайл(), передавая ему в качестве первого параметра локальный путь к файлу на клиентском компьютере (например, C:\Users\User\AppData\Local\Temp\v8_83BC_28.txt). Однако ПолучитьФайл() предназначен для получения файла из временного хранилища 1С на сервере, и первым параметром он ожидает именно адрес в этом хранилище (строку вида "e1c8d..."), а не локальный путь. Функция ВыгрузкаДанныхЛокально создает временный файл на клиентском компьютере и возвращает его локальный путь, что является некорректным входным параметром для ПолучитьФайл().

Таким образом, ПолучитьФайл() не работает с локальными путями для переноса файлов внутри клиентской файловой системы, а служит для скачивания файла с сервера на клиент. Использование ПолучитьИмяВременногоФайла() на клиенте для создания файла, а затем попытка передать его же в ПолучитьФайл(), приводит к неверной логике, так как эти функции выполняют разные задачи и ожидают разные типы параметров.

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

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

  1. Расширение для работы с файлами. Для некоторых версий платформы и специфических операций с файлами (например, для интерактивного выбора пути сохранения без вызова стандартного диалога браузера) в веб-клиенте может потребоваться установка специального расширения для браузера 1С:Предприятия. Однако для базовой операции "скачать файл", как правило, достаточно стандартного механизма браузера, вызываемого ПолучитьФайл() с интерактивным режимом или НачатьПолучениеФайлаССервера(). Мы рекомендуем по возможности обходиться без расширений, используя встроенные возможности платформы и браузера.
  2. Асинхронные методы. Всегда отдавайте предпочтение асинхронным методам (НачатьПомещениеФайлаНаСервер(), НачатьПолучениеФайлаССервера()) при работе с файлами в веб-клиенте. Они обеспечивают лучшую отзывчивость интерфейса, позволяют обрабатывать большие файлы без "зависания" пользовательского интерфейса и более корректно взаимодействуют с пользовательскими диалогами браузера. Эти методы значительно улучшают пользовательский опыт, особенно при работе с медленным соединением или большими объемами данных.
  3. Ограничения размера файла. При передаче очень больших файлов через веб-сервер могут возникать ограничения, устанавливаемые самим веб-сервером (например, IIS, Apache) или браузером. Обычно они касаются максимального размера HTTP-запроса или ответа. В таких случаях для обхода ограничений может потребоваться разбивать файл на части на сервере, передавать их по отдельности и собирать уже на клиенте, либо настраивать параметры веб-сервера (например, увеличить maxRequestLength в IIS или LimitRequestBody в Apache). Важно заранее оценить потенциальный размер файлов, с которыми вы будете работать.
  4. Кодировка текста. Всегда указывайте кодировку текста (например, КодировкаТекста.UTF8) при записи и чтении текстовых файлов, чтобы избежать проблем с отображением символов, особенно при обмене данными между различными операционными системами (Windows на клиенте, Linux на сервере) или приложениями. Использование универсальной кодировки UTF8 является лучшей практикой.
  5. Обработка ошибок. Всегда включайте блоки Попытка...Исключение...КонецПопытки при работе с файловой системой и передачей данных. Это позволит корректно обрабатывать ситуации, когда файл не найден, доступ запрещен, или произошла ошибка сети, информируя пользователя и предотвращая падение системы.

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

← На главную