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

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

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

Понимание ограничений безопасного режима при работе с файлами

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


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


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

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

Решение: Использование механизма временного хранилища

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

Давайте разберем по шагам, как правильно реализовать загрузку файла через временное хранилище.

  1. Шаг 1: Выбор файла на клиенте

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

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

    В этом коде мы создаем диалог выбора файла в режиме открытия. Устанавливаем заголовок, фильтр для файлов (например, для файлов с расширением MXL) и указываем, что множественный выбор не разрешен. Затем вызываем метод Показать(), передавая ему описание оповещения. Это означает, что после того, как пользователь выберет файл (или отменит выбор), будет вызвана процедура ЗагрузитьФайлЗавершение.

  2. Шаг 2: Чтение файла на клиенте и помещение его содержимого во временное хранилище

    Когда пользователь выбирает файл, вызывается процедура ЗагрузитьФайлЗавершение. В этой процедуре мы получаем путь к выбранному файлу, читаем его содержимое на клиенте, создавая объект ТабличныйДокумент, и затем помещаем этот объект во временное хранилище. Во временное хранилище помещается не путь к файлу, а именно его содержимое или объект, который его представляет.

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

    Здесь ключевым моментом является строка ТаблДокумент.Прочитать(Диалог.ПолноеИмяФайла);. Она выполняется на клиенте, где нет ограничений безопасного режима на доступ к файлам пользователя. После успешного чтения, сам объект ТаблДокумент, содержащий данные файла, передается в серверный контекст через функцию ПоместитьВоВременноеХранилище(ТаблДокумент). Эта функция возвращает уникальный адрес, по которому этот объект можно будет получить на сервере. Мы передаем этот адрес в серверную процедуру ОбработатьФайлНаСервере().

  3. Шаг 3: Получение данных из временного хранилища на сервере и их обработка

    На сервере, в процедуре, которая должна обработать данные файла, мы используем полученный адрес, чтобы извлечь объект из временного хранилища (например, для последующей конвертации данных из JSON/XML/CSV). Это делается с помощью функции ПолучитьИзВременногоХранилища().

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

    В этой серверной процедуре ТаблДокумент — это уже готовый объект, который был прочитан на клиенте. Мы не выполняем никаких операций с файловой системой на сервере, а просто работаем с данными, которые находятся в оперативной памяти сервера, будучи ранее переданными через временное хранилище. Это полностью обходит ограничения безопасного режима для файловых операций.

Полный пример реализации загрузки табличного документа в безопасном режиме

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


// Процедура для вызова диалога выбора файла на клиенте
&НаКлиенте
Процедура ЗагрузитьФайл(Команда)
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    Диалог.Заголовок = "Выберите файл MXL";
    Диалог.ПолноеИмяФайла = "";
    Фильтр = "MXL (*.mxl)|*.mxl"; // Указываем фильтр для файлов MXL
    Диалог.Фильтр = Фильтр;
    Диалог.МножественныйВыбор = Ложь;
    // Показываем диалог и указываем процедуру, которая будет вызвана после выбора файла
    Диалог.Показать(Новый ОписаниеОповещения("ЗагрузитьФайлЗавершение", ЭтаФорма, Новый Структура("Диалог", Диалог)));
КонецПроцедуры

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

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

    Исключение
        Сообщить("Ошибка при получении или обработке данных на сервере: " + ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

В этом примере, мы видим полный цикл работы: от выбора файла пользователем на клиенте до обработки его содержимого на сервере, минуя ограничения безопасного режима. Важно помнить, что ПоместитьВоВременноеХранилище может принимать второй необязательный параметр - УникальныйИдентификатор. Если его не указать, система сгенерирует его автоматически. Однако, если мы хотим иметь возможность повторно обратиться к одним и тем же данным из разных мест или управлять сроком жизни данных в хранилище, указание собственного идентификатора может быть полезным.

Почему такой подход безопасен и рекомендован?

Использование временного хранилища, через которое может быть реализована универсальная выгрузка и загрузка данных с шифрованием, является основным и наиболее безопасным методом, поскольку оно:

  1. Изолирует файловые операции: Непосредственное чтение файла с диска происходит только на клиентской стороне, где пользователь имеет явные права доступа к своим файлам. Сервер не взаимодействует с файловой системой напрямую.
  2. Совместимо с безопасным режимом: Безопасный режим разрешает передачу данных через временное хранилище, так как это контролируемый платформой механизм.
  3. Обеспечивает универсальность: Подход работает одинаково эффективно для различных типов клиентов (толстый, тонкий, веб), что позволяет, к примеру, реализовать работу с PDF без внешних компонентов, так как временное хранилище абстрагируется от особенностей операционной системы и расположения файлов.
  4. Просто в реализации: Методы ПоместитьВоВременноеХранилище() и ПолучитьИзВременногоХранилища() интуитивно понятны и просты в использовании.

Альтернативные (нерекомендуемые) подходы: предоставление разрешений

В некоторых очень специфических случаях, когда прямой доступ к файловой системе сервера *действительно* необходим (например, для регламентных заданий, которые обрабатывают файлы на самом сервере, или для интеграций, где сервер должен читать/писать в определенные каталоги), можно рассмотреть возможность предоставления дополнительных разрешений. Однако такой подход менее безопасен и не рекомендуется без острой необходимости.

Механизмы предоставления разрешений включают:

  1. Профили безопасности: В конфигурации 1С можно настроить профили безопасности, которые предоставляют разрешения на использование определенных каталогов файловой системы. Это позволяет явно указать, какие каталоги доступны для чтения/записи серверными процессами. Например, можно дать разрешение на использование каталога временных файлов для чтения и записи.
  2. Отключение "Защиты от опасных действий": Для конкретных пользователей или для выполнения регламентных заданий под определенным пользователем с полными правами можно отключить "Защиту от опасных действий". Это полностью снимает ограничения безопасного режима, что делает систему значительно менее защищенной и является крайней мерой, которую следует применять с особой осторожностью и только для доверенных и проверенных участков кода.
  3. Программное добавление разрешений: Для внешних обработок и отчетов разрешения могут быть добавлены программно при их регистрации, но это также требует понимания рисков и контроля за кодом.

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

← На главную