В работе с платформой 1С:Предприятие 8, особенно при использовании веб-клиента и облачных баз, возникает частая задача: программное сохранение текстового файла на локальный компьютер пользователя. На первый взгляд задача кажется простой, ведь в файловой базе или при работе в тонком клиенте мы можем легко использовать метод ТекстовыйДокумент.Записать(), указав прямой путь к файлу на диске. Однако, когда речь заходит о работе через веб-сервер, этот подход перестает работать, и мы сталкиваемся с ошибками, как и наш коллега в исходном сообщении.
Давайте выясним, почему привычный нам код, который отлично работает на локальном ПК с файловой базой, выдает ошибку при попытке выполнить его на веб-сервере. Проанализируем ситуацию и выясним причины.
ТекстовыйДокумент.Записать(ПутьКФайлу, ...) пытается сохранить файл по указанному пути ПутьКФайлу на той машине, где выполняется этот код. Если код выполняется на сервере (например, в функции &НаСервере), он ищет путь на диске сервера. Если же этот путь указывает на локальный диск пользователя (например, C:\МояПапка\файл.txt), серверная машина ничего не знает о таком пути. У нее может быть другая операционная система (например, Linux, как упоминается в одном из сообщений), и диск C для нее просто не существует.
ФайлTXT.Записать( Объект.СсылкаНаФайл + "\НоменклатураОстаткиИЦены.txt", ...), переменная Объект.СсылкаНаФайл, вероятно, содержит путь к файлу на локальном ПК пользователя. Когда этот код выполняется на сервере, сервер пытается записать файл по пути, который для него не существует или является некорректным. В итоге мы получаем ошибку, потому что сервер не может найти указанный путь на своей файловой системе.
Для успешного сохранения файла на локальный компьютер пользователя из веб-клиента 1С мы должны использовать двухэтапный подход, который предусматривает взаимодействие между сервером и клиентом через механизм временного хранения данных. Этот метод гарантирует безопасность и корректную передачу файлов. Рассмотрим его подробнее по шагам.
Первым делом необходимо сгенерировать сам текстовый файл. Это действие должно выполняться на сервере 1С. Мы не можем сразу передать "виртуальный" текстовый документ на клиент для сохранения, поскольку клиент ожидает уже готовые к записи двоичные данные или файл по адресу во временном хранилище.
ПолучитьИмяВременногоФайла(), чтобы получить уникальное имя для временного файла на диске сервера. Эта функция генерирует имя файла в системной временной папке, что гарантирует, что мы не перезапишем существующие файлы и что файл будет создан в месте, доступном для серверного процесса.ТекстовыйДокумент, устанавливаем в него наши текстовые данные (например, из переменной ТекстовыеДанные) и записываем этот документ в полученный временный файл на диске сервера. Важно указать кодировку текста, например, КодировкаТекста.UTF8, чтобы избежать проблем с символами при дальнейшем использовании файла.ДвоичныеДанные. Это универсальный формат для передачи файлов внутри системы 1С, который может быть легко сериализован и передан между контекстами.ДвоичныеДанные помещаются во ВременноеХранилище 1С с помощью функции ПоместитьВоВременноеХранилище(). Эта функция возвращает уникальный адрес (строку), по которому эти данные будут доступны в течение определенного времени. Этот адрес — ключ, который мы передадим клиенту. Мы можем использовать любой уникальный идентификатор или просто Неопределено в качестве второго параметра, чтобы 1С сама сгенерировала его.УдалитьФайлы().После того как файл "подготовлен" и помещен во временное хранилище на сервере, мы можем уведомить клиент о его готовности и передать адрес хранения. Затем клиентский код выполнит сохранение файла на локальный диск пользователя.
ПолучитьФайл(). На клиенте вызываем глобальный метод ПолучитьФайл(), передав ему адрес из временного хранилища в качестве первого параметра. Вторым параметром указываем предлагаемое имя файла для сохранения (например, "НоменклатураОстаткиИЦены.txt"), а третьим (необязательным) — Истина для включения интерактивного режима, который вызовет стандартный диалог сохранения файла в браузере. Без третьего параметра файл может быть автоматически сохранен в папку загрузок браузера с именем по умолчанию.НачатьПолучениеФайлаССервера(). Эти методы более оптимальны для веб-клиента, не требуют установки расширений для работы с файлами и позволяют работать с файлами больших размеров без зависания интерфейса. Они принимают ОписаниеОповещения, которое позволяет обработать результат сохранения файла после его завершения.Давайте рассмотрим конкретные примеры кода, демонстрирующие описанный двухэтапный процесс. Мы увидим, как правильно организовать взаимодействие между сервером и клиентом.
Серверный код: Формирование и помещение файла во временное хранилище
&НаСервере
// Функция для формирования текстовых данных, создания временного файла
// на сервере, помещения его во временное хранилище и возврата адреса.
// Параметры:
// ТекстовыеДанные - Строка, содержимое текстового файла.
// УникальныйИдентификатор - УникальныйИдентификатор или Строка для идентификации данных в хранилище.
// Возвращаемое значение:
// Строка - адрес файла во временном хранилище.
Функция СформироватьИПоместитьФайлНаСервере(ТекстовыеДанные, УникальныйИдентификатор = Неопределено)
Перем ИмяВременногоФайлаНаСервере;
Попытка
// 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С, чтобы обеспечить максимальную стабильность и удобство для пользователя.
ПолучитьФайл() с интерактивным режимом или НачатьПолучениеФайлаССервера(). Мы рекомендуем по возможности обходиться без расширений, используя встроенные возможности платформы и браузера.
НачатьПомещениеФайлаНаСервер(), НачатьПолучениеФайлаССервера()) при работе с файлами в веб-клиенте. Они обеспечивают лучшую отзывчивость интерфейса, позволяют обрабатывать большие файлы без "зависания" пользовательского интерфейса и более корректно взаимодействуют с пользовательскими диалогами браузера. Эти методы значительно улучшают пользовательский опыт, особенно при работе с медленным соединением или большими объемами данных.
maxRequestLength в IIS или LimitRequestBody в Apache). Важно заранее оценить потенциальный размер файлов, с которыми вы будете работать.
КодировкаТекста.UTF8) при записи и чтении текстовых файлов, чтобы избежать проблем с отображением символов, особенно при обмене данными между различными операционными системами (Windows на клиенте, Linux на сервере) или приложениями. Использование универсальной кодировки UTF8 является лучшей практикой.
Попытка...Исключение...КонецПопытки при работе с файловой системой и передачей данных. Это позволит корректно обрабатывать ситуации, когда файл не найден, доступ запрещен, или произошла ошибка сети, информируя пользователя и предотвращая падение системы.
Таким образом, сохранение текстового файла из 1С, работающей через веб-сервер, на локальный ПК пользователя — это задача, которая требует понимания архитектуры клиент-серверного взаимодействия и использования специфических механизмов платформы. Следуя описанным шагам и примерам, мы можем успешно реализовать эту функциональность, обеспечивая стабильность и безопасность работы, а также высокий уровень удобства для конечного пользователя. Для интерактивной передачи данных между файловыми системами можно использовать файловый менеджер для обмена файлами между клиентом и сервером.