Каталог решений - Выбор файла и загрузка его на сервер в асинхронном режиме

Выбор файла и загрузка его на сервер в асинхронном режиме

Выбор файла и загрузка его на сервер в асинхронном режиме

В наличии

Несколько библиотечных процедур, упрощающих жизнь разработчика при отключенном режиме модальности.

Категория:

Описание

Введение

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

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

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

Диалог выбора файла

Сразу приведу код, который получился после упрощения.

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

&НаКлиенте
Процедура КаталогНачалоВыбораЗавершение(Результат, ДополнительныеПараметры) Экспорт
    Если Результат = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    Объект.Каталог = Результат[0];
    
КонецПроцедуры

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

Далее приведу код этой служебной процедуры, написанной по принципу, из этой статьи. 

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

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

Загрузка файлов с клиента на сервер

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

Данная задача снова решается за три простых шага

&НаКлиенте
Процедура ОбработатьФайлы(Команда)

    ПараметрыСценария = Новый Структура;
    Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайлаНаСервер", ЭтаФорма);
    ПараметрыСценария.Вставить("Оповещение_ПослеПомещенияФайлаНаСервер", Оповещение);
    
    ВыполнитьСценарийЗагрузкиФайлаНаСервер(Объект.Каталог, ПараметрыСценария);
    
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайлаНаСервер(ПомещенныеФайлы, ДополнительныеПараметры) Экспорт
    
    ОбработатьФайлНаСервере(ПомещенныеФайлы);
    
КонецПроцедуры

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

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

Исходный код
&НаКлиенте
Процедура ВыполнитьСценарийЗагрузкиФайлаНаСервер(Результат = Неопределено, ПараметрыСценария = Неопределено) Экспорт
    
    НачатьОтсчетШагов(ПараметрыСценария);
    
    Оповещение = Новый ОписаниеОповещения("ВыполнитьСценарийЗагрузкиФайлаНаСервер", ЭтаФорма, ПараметрыСценария);
    
    // определим что на входе: файл или каталог
    Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
        ПараметрыСценария.Вставить("ИсходныйПомещаемыйФайл", Результат);
        Файл = Новый Файл(ПараметрыСценария.ИсходныйПомещаемыйФайл);
        Файл.НачатьПроверкуЭтоФайл(Оповещение);
        Возврат;
    КонецЕсли;
    
    // если передан каталог, то нужно найти все файлы, содеражщиеся в нем
    Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
        Если Результат = Истина Тогда // если это не каталог, то переходим к следующему шагу
            ПомещаемыеФайлы = Новый Массив;
            ПомещаемыеФайлы.Добавить(Новый Файл(ПараметрыСценария.ИсходныйПомещаемыйФайл));
            ВыполнитьОбработкуОповещения(Оповещение, ПомещаемыеФайлы);
            Возврат;
        Иначе
            // выполняем поиск всех файлов, содержащихся в переданном каталоге
            НачатьПоискФайлов(Оповещение, ПараметрыСценария.ИсходныйПомещаемыйФайл, "*.?*");
            Возврат;
        КонецЕсли;
    КонецЕсли;
    
    // проверяем, что найденные файлы не являются каталогами
    Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
        ПараметрыСценария.Вставить("НайденныеФайлы", Результат);
        ПараметрыСценария.Вставить("ПомещаемыеФайлы", Новый Массив);
        ПараметрыСценария.Вставить("ИндексПроверяемогоФайла", 0);
        Результат[ПараметрыСценария.ИндексПроверяемогоФайла].НачатьПроверкуЭтоФайл(Оповещение);
        Возврат;
        
    // асинхронная проверка каждого из найденных файлов
    ИначеЕсли ЭтоРезультатВыполненногоШага(ПараметрыСценария) Тогда
        
        Если Результат = Истина Тогда
            Файл = ПараметрыСценария.НайденныеФайлы[ПараметрыСценария.ИндексПроверяемогоФайла];
            ПараметрыСценария.ПомещаемыеФайлы.Добавить(Новый ОписаниеПередаваемогоФайла(Файл.ПолноеИмя, ""));
        КонецЕсли;
        
        Если ПараметрыСценария.НайденныеФайлы.Количество() > ПараметрыСценария.ИндексПроверяемогоФайла+1 Тогда
            ПараметрыСценария.ИндексПроверяемогоФайла = ПараметрыСценария.ИндексПроверяемогоФайла + 1;
            Файл = ПараметрыСценария.НайденныеФайлы[ПараметрыСценария.ИндексПроверяемогоФайла];
            Файл.НачатьПроверкуЭтоФайл(Оповещение);
            Возврат;
        КонецЕсли;
        
    КонецЕсли;
    
    // помещаем файлы на сервер
    Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда
        
        ПомещаемыеФайлы = ПараметрыСценария.ПомещаемыеФайлы;
        Если ПомещаемыеФайлы.Количество() Тогда
            // передаем управление в прикладную процедуру
            НачатьПомещениеФайлов(ПараметрыСценария.Оповещение_ПослеПомещенияФайлаНаСервер, ПомещаемыеФайлы, , Ложь);    
        КонецЕсли;
        
        Возврат;
    КонецЕсли;
    
КонецПроцедуры

Файл для скачивания

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

 

has been added to your cart:
Оформление заказа