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

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

В процессе разработки в среде 1С:Предприятие часто возникает задача, когда пользователю на клиенте необходимо выбрать файл, физически расположенный в определенном каталоге на сервере (например, в так называемой «серверной помойке», куда выгружаются файлы обмена). Напрямую использовать стандартный ДиалогВыбораФайла в этой ситуации не получится, так как он работает исключительно в контексте клиента и «видит» только локальные диски пользователя или доступные ему сетевые ресурсы.

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

Почему стандартный ДиалогВыбораФайла не решает задачу напрямую?

Объект ДиалогВыбораФайла инициализируется и выполняется строго на стороне клиента (&НаКлиенте). Он обращается к операционной системе пользователя. Если папка с файлами находится на сервере приложений 1С, клиентский компьютер просто не имеет к ней прямого доступа, если только эта папка не «расшарена» по сети (через UNC-путь вида \\server\share\).

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

Общая схема решения задачи

Для реализации выбора файла без предоставления пользователю прямого доступа к серверной папке мы применим трехэтапную схему:

  1. На сервере: Сканируем каталог с помощью функции НайтиФайлы(), собираем информацию о файлах и упаковываем ее в сериализуемый массив простых структур.
  2. На клиенте: Получаем этот массив и выводим его пользователю для интерактивного выбора (через простой список или специальную форму).
  3. Передача файла (при необходимости): Если файл нужно не просто переименовать или зафиксировать его имя, а прочитать на клиенте — помещаем его в транзитное временное хранилище на сервере и забираем на клиенте.

Рассмотрим каждый из этих этапов подробнее.

Шаг 1. Получение списка файлов на сервере

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

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


&НаСервереБезКонтекста
Функция ПолучитьСписокФайловСервера(Знач ПутьКПапке, Знач МаскаПоиска = "*.xml") Экспорт

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

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

В этом коде мы обезопасили передачу данных, преобразовав объекты типа Файл в простые структуры, состоящие из строк, чисел и дат. Также мы использовали метод ПолучитьРазделительПути(), что обеспечивает корректную работу кода как на операционных системах Windows, так и на Linux.

Шаг 2. Интерактивный выбор файла на клиенте

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

Вариант А. Быстрый выбор через Список Значений (без создания форм)

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


&НаКлиенте
Процедура ВыбратьФайлНаСервереБыстро()

    ПутьКПапке = "C:\Exchange\XML_Import\"; // Этот путь должен храниться в константах или настройках
    
    МассивФайлов = ПолучитьСписокФайловСервера(ПутьКПапке, "*.xml");
    
    Если МассивФайлов.Количество() = 0 Тогда
        ПоказатьПредупреждение(, "В указанной папке на сервере нет подходящих файлов!");
        Возврат;
    КонецЕсли;
    
    СписокВыбора = Новый СписокЗначений;
    Для Каждого ФайлИнфо Из МассивФайлов Цикл
        // Формируем понятное представление для пользователя (Имя + размер в Кб)
        РазмерКб = Округление(ФайлИнфо.Размер / 1024, 1);
        Представление = СтрШаблон("%1 (%2 Кб, %3)", ФайлИнфо.Имя, РазмерКб, Формат(ФайлИнфо.ДатаИзменения, "ДФ='dd.MM.yyyy HH:mm'"));
        
        СписокВыбора.Добавить(ФайлИнфо.ПолноеИмя, Представление);
    КонецЦикла;
    
    // Описываем обработчик оповещения после выбора
    Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайлаИзСписка", ЭтотОбъект);
    ПоказатьВыборИзСписка(Оповещение, СписокВыбора);

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

&НаКлиенте
Процедура ПослеВыбораФайлаИзСписка(ВыбранныйЭлемент, ДополнительныеПараметры) Экспорт

    Если ВыбранныйЭлемент = Неопределено Тогда
        Возврат; // Пользователь закрыл список выбора
    КонецЕсли;
    
    ПолныйПутьКВыбранномуФайлу = ВыбранныйЭлемент.Значение;
    ПоказатьПутьКФайлуПользователю(ПолныйПутьКВыбранномуФайлу);
    
    // Далее можем запускать обработку этого файла на сервере
    ОбработатьВыбранныйФайлНаСервере(ПолныйПутьКВыбранномуФайлу);

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

Вариант Б. Создание собственной формы выбора (для продвинутых интерфейсов)

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

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

Шаг 3. Передача выбранного файла на клиент для просмотра

Что делать, если пользователю мало просто запустить обработку файла на сервере, а требуется открыть его на своем клиентском компьютере для визуального анализа? В этом случае необходимо передать двоичные данные файла через временное хранилище.

Проанализируем пример кода, выполняющего эту задачу:


&НаСервереБезКонтекста
Функция ПоместитьФайлВоВременноеХранилище(Знач ПолныйПутьКФайлу) Экспорт

    ДвоичныеДанныеФайла = Новый ДвоичныеДанные(ПолныйПутьКФайлу);
    АдресХранилища = ПоместитьВоВременноеХранилище(ДвоичныеДанныеФайла);
    
    Возврат АдресХранилища;

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

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

    // Шаг 1: Помещаем файл в хранилище на сервере
    АдресХранилища = ПоместитьФайлВоВременноеХранилище(ПолныйПутьКФайлу);
    
    // Шаг 2: Получаем локальный каталог для сохранения временного файла на клиенте
    ЛокальныйКаталог = КаталогВременныхФайлов();
    ЛокальныйПуть = ЛокальныйКаталог + ИмяФайла;
    
    // Шаг 3: Получаем двоичные данные на клиент и сохраняем на диск
    ДвоичныеДанныеКлиент = ПолучитьИзВременногоХранилища(АдресХранилища);
    ДвоичныеДанныеКлиент.Записать(ЛокальныйПуть);
    
    // Шаг 4: Запускаем приложение, ассоциированное с типом файла в ОС клиента
    ЗапуститьПриложение(ЛокальныйПуть);

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

Критически важные нюансы при работе с сервером

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

  1. Права доступа службы 1С:Предприятие. Поиск файлов и их чтение на сервере выполняются от имени той учетной записи операционной системы, под которой запущена служба сервера 1С (обычно это локальный пользователь USR1CV8 на Windows или usr1cv8 на Linux). Убедитесь, что у этого пользователя настроены права доступа (чтение и запись) к целевой папке на уровне операционной системы.
  2. Безопасность и уязвимость Directory Traversal. Если путь к каталогу формируется динамически или передается с клиента, злонамеренный пользователь может передать в параметрах относительные пути вроде ..\..\..\Windows\ и получить доступ к конфиденциальным файлам сервера. Всегда жестко прописывайте базовый разрешенный каталог на сервере или проверяйте, что итоговый путь не выходит за рамки разрешенной директории.
  3. Кроссплатформенность путей. Помните, что разделители каталогов в Windows (\) и Linux (/) различаются. При конкатенации путей всегда используйте системные функции платформы 1С для автоматического определения разделителя.

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

← На главную