При разработке решений на платформе 1С:Предприятие версии 8.3, особенно при работе с управляемыми формами и тонкими/веб-клиентами, часто возникает необходимость обработки файлов. Однако, столкнувшись с "Ошибкой безопасного режима", многие разработчики задаются вопросом, как правильно организовать работу с файлами, чтобы избежать подобных проблем. Давайте рассмотрим этот вопрос подробнее, выясним причину ошибки и разберем корректный способ ее решения, используя рекомендованные механизмы платформы.
Безопасный режим в 1С:Предприятии — это важный механизм защиты, предназначенный для предотвращения выполнения потенциально опасного кода, особенно при использовании внешних обработок, отчетов или кода, динамически создаваемого пользователем. Этот режим накладывает ряд существенных ограничений, одним из которых является запрет прямого доступа к файловой системе сервера. Когда мы пытаемся выполнить операции с файлами на сервере, например, прочитать файл с помощью метода ТабличныйДокумент.Прочитать(ИмяФайла), как это показано в исходном коде из
Сообщение 1
// Фрагмент кода из Сообщения 1, приводящий к ошибке в безопасном режиме
&НаСервере
Процедура ЗагрузитьДанныеНаСервере(Результат, ДопПараметр)
// ...
ИмяФайла = Результат[0]; // Полученное имя файла
// ...
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяФайла); // Вот здесь возникает ошибка безопасного режима!
// ...
КонецПроцедуры
В приведенном примере, переменная ИмяФайла содержит полный путь к файлу на клиентской машине. Когда мы пытаемся использовать этот путь на сервере в безопасном режиме с методом ТабДок.Прочитать(), платформа не позволяет это сделать, так как серверный код не имеет прямого доступа к локальной файловой системе клиента, и тем более не имеет разрешения на чтение произвольных файлов с сервера без явного разрешения. Наша задача — найти способ передать содержимое файла с клиента на сервер, не нарушая этих правил.
Платформа 1С:Предприятие предоставляет надежный и безопасный механизм для обмена данными между клиентским и серверным контекстом, особенно при работе с файлами. Это временное хранилище. Этот механизм позволяет нам временно хранить данные, привязанные к сеансу пользователя, и безопасно передавать их между клиентом и сервером. Такой подход полностью соответствует принципам безопасного режима, поскольку непосредственные операции с файловой системой выполняются на клиенте, где у пользователя есть права на доступ к своим файлам, а на сервер передается уже сам объект с данными, а не путь к файлу.
Давайте разберем по шагам, как правильно реализовать загрузку файла через временное хранилище.
Первым делом мы должны позволить пользователю выбрать файл на его локальном компьютере. Для этого используется объект ДиалогВыбораФайла, который работает исключительно в клиентском контексте.
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выберите файл";
Диалог.ПолноеИмяФайла = "";
Фильтр = "MXL (*.mxl)|*.mxl"; // Пример фильтра для файлов MXL
Диалог.Фильтр = Фильтр;
Диалог.МножественныйВыбор = Ложь;
Диалог.Показать(Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтаФорма, Новый Структура("Диалог", Диалог)));
КонецПроцедуры
В этом коде мы создаем диалог выбора файла в режиме открытия. Устанавливаем заголовок, фильтр для файлов (например, для файлов с расширением MXL) и указываем, что множественный выбор не разрешен. Затем вызываем метод Показать(), передавая ему описание оповещения. Это означает, что после того, как пользователь выберет файл (или отменит выбор), будет вызвана процедура ЗагрузитьФайлЗавершение.
Когда пользователь выбирает файл, вызывается процедура ЗагрузитьФайлЗавершение. В этой процедуре мы получаем путь к выбранному файлу, читаем его содержимое на клиенте, создавая объект ТабличныйДокумент, и затем помещаем этот объект во временное хранилище. Во временное хранилище помещается не путь к файлу, а именно его содержимое или объект, который его представляет.
&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Диалог = ДополнительныеПараметры.Диалог;
Если (ВыбранныеФайлы <> Неопределено) Тогда
ТаблДокумент= Новый ТабличныйДокумент;
// Читаем файл на клиенте, где доступ к файловой системе разрешен пользователю
ТаблДокумент.Прочитать(Диалог.ПолноеИмяФайла);
// Помещаем прочитанный объект ТабличныйДокумент во временное хранилище
АдресВХ = ПоместитьВоВременноеХранилище(ТаблДокумент);
// Вызываем серверную процедуру, передавая адрес в хранилище
ОбработатьФайлНаСервере(АдресВХ);
КонецЕсли;
КонецПроцедуры
Здесь ключевым моментом является строка ТаблДокумент.Прочитать(Диалог.ПолноеИмяФайла);. Она выполняется на клиенте, где нет ограничений безопасного режима на доступ к файлам пользователя. После успешного чтения, сам объект ТаблДокумент, содержащий данные файла, передается в серверный контекст через функцию ПоместитьВоВременноеХранилище(ТаблДокумент). Эта функция возвращает уникальный адрес, по которому этот объект можно будет получить на сервере. Мы передаем этот адрес в серверную процедуру ОбработатьФайлНаСервере().
На сервере, в процедуре, которая должна обработать данные файла, мы используем полученный адрес, чтобы извлечь объект из временного хранилища (например, для последующей конвертации данных из JSON/XML/CSV). Это делается с помощью функции ПолучитьИзВременногоХранилища().
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресВХ)
// Получаем объект ТабличныйДокумент из временного хранилища по адресу
ТаблДокумент = ПолучитьИзВременногоХранилища(АдресВХ);
// Теперь мы можем работать с данными файла на сервере,
// так как они были переданы в виде объекта, а не прочитаны напрямую с файловой системы
Для Строка = 1 По ТаблДокумент.ВысотаТаблицы Цикл
Сообщить(ТаблДокумент.Область(Строка, 2).Текст);
КонецЦикла;
КонецПроцедуры
В этой серверной процедуре ТаблДокумент — это уже готовый объект, который был прочитан на клиенте. Мы не выполняем никаких операций с файловой системой на сервере, а просто работаем с данными, которые находятся в оперативной памяти сервера, будучи ранее переданными через временное хранилище. Это полностью обходит ограничения безопасного режима для файловых операций.
Давайте скомбинируем все части в единый, рабочий пример (по принципу которого работает адаптивная выгрузка и загрузка данных XML), который позволяет безопасно загружать табличные документы (например, .mxl файлы) в 1С, даже когда клиент подключен в безопасном режиме (например, через веб-клиент или тонкий клиент без полных прав).
// Процедура для вызова диалога выбора файла на клиенте
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Заголовок = "Выберите файл MXL";
Диалог.ПолноеИмяФайла = "";
Фильтр = "MXL (*.mxl)|*.mxl"; // Указываем фильтр для файлов MXL
Диалог.Фильтр = Фильтр;
Диалог.МножественныйВыбор = Ложь;
// Показываем диалог и указываем процедуру, которая будет вызвана после выбора файла
Диалог.Показать(Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтаФорма, Новый Структура("Диалог", Диалог)));
КонецПроцедуры
// Процедура, вызываемая после выбора файла на клиенте
&НаКлиенте
Процедура ЗагрузитьФайлЗавершение(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Диалог = ДополнительныеПараметры.Диалог;
// Проверяем, был ли файл выбран
Если (ВыбранныеФайлы <> Неопределено) Тогда
ТаблДокумент = Новый ТабличныйДокумент;
Попытка
// Читаем содержимое файла на клиенте
ТаблДокумент.Прочитать(Диалог.ПолноеИмяФайла);
// Помещаем объект ТабличныйДокумент во временное хранилище и получаем его адрес
АдресВХ = ПоместитьВоВременноеХранилище(ТаблДокумент, УникальныйИдентификатор);
// Вызываем серверную процедуру для обработки данных, передавая ей адрес в хранилище
ОбработатьФайлНаСервере(АдресВХ);
Исключение
Сообщить("Ошибка при чтении файла на клиенте: " + ОписаниеОшибки());
КонецПопытки;
Иначе
Сообщить("Файл не выбран.");
КонецЕсли;
КонецПроцедуры
// Серверная процедура для обработки данных, полученных из временного хранилища
&НаСервере
Процедура ОбработатьФайлНаСервере(АдресВХ)
Попытка
// Получаем объект ТабличныйДокумент из временного хранилища по переданному адресу
ТаблДокумент = ПолучитьИзВременногоХранилища(АдресВХ);
// Далее выполняем необходимую логику обработки данных табличного документа на сервере
// Например, обход строк и вывод их содержимого:
Сообщить("Начало обработки данных на сервере:");
Для Строка = 1 По ТаблДокумент.ВысотаТаблицы Цикл
// Пример: выводим текст из второй колонки каждой строки
Сообщить("Строка " + Строка + ": " + ТаблДокумент.Область(Строка, 2).Текст);
// Здесь может быть ваша логика по записи данных в справочники, документы и т.д.
// Например:
// Если НЕ ТаблДокумент.Область(Строка, 2).Текст = "" Тогда
// ЭлементыВнеоборотныеАктивы = Справочники.ВнеоборотныеАктивы.СоздатьЭлемент();
// ЭлементыВнеоборотныеАктивы.Наименование = ТаблДокумент.Область(Строка, 2).Текст;
// ЭлементыВнеоборотныеАктивы.Записать();
// КонецЕсли;
КонецЦикла;
Сообщить("Обработка данных завершена успешно.");
Исключение
Сообщить("Ошибка при получении или обработке данных на сервере: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
В этом примере, мы видим полный цикл работы: от выбора файла пользователем на клиенте до обработки его содержимого на сервере, минуя ограничения безопасного режима. Важно помнить, что ПоместитьВоВременноеХранилище может принимать второй необязательный параметр - УникальныйИдентификатор. Если его не указать, система сгенерирует его автоматически. Однако, если мы хотим иметь возможность повторно обратиться к одним и тем же данным из разных мест или управлять сроком жизни данных в хранилище, указание собственного идентификатора может быть полезным.
Использование временного хранилища, через которое может быть реализована универсальная выгрузка и загрузка данных с шифрованием, является основным и наиболее безопасным методом, поскольку оно:
ПоместитьВоВременноеХранилище() и ПолучитьИзВременногоХранилища() интуитивно понятны и просты в использовании.В некоторых очень специфических случаях, когда прямой доступ к файловой системе сервера *действительно* необходим (например, для регламентных заданий, которые обрабатывают файлы на самом сервере, или для интеграций, где сервер должен читать/писать в определенные каталоги), можно рассмотреть возможность предоставления дополнительных разрешений. Однако такой подход менее безопасен и не рекомендуется без острой необходимости.
Механизмы предоставления разрешений включают:
Мы настоятельно рекомендуем всегда отдавать предпочтение механизму временного хранилища при работе с файлами в управляемых формах, особенно если операции инициируются с клиентской стороны. Предоставление дополнительных разрешений или отключение средств защиты должно рассматриваться только как крайняя мера для серверных операций, не связанных напрямую с взаимодействием с пользователем через клиентское приложение, и требует глубокого анализа потенциальных угроз безопасности.