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

22.10.17

Разработка - Универсальные функции

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

Файлы

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Купить файл
Выбор файла и загрузка его на сервер в асинхронном режиме 8.3 (УФ+ОФ)
.epf 13,67Kb
125 1 850 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Введение

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

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


 

Вступайте в нашу телеграмм-группу Инфостарт

Выбор файла выбрать файл выбор файла уф выбрать файл уф

См. также

Загрузка и выгрузка в Excel Универсальные функции Программист 1С:Предприятие 8 Россия Бесплатно (free)

Описанный ниже подход позволяет в три шага заполнять формулы в Excel файлы, вне зависимости от ОС сервера (MS Windows Server или Linux). Подход подразумевает отказ от работы с COM-объектом в пользу работы через "объектную модель документа" (DOM).

30.10.2025    3369    Abysswalker    8    

44

Универсальные функции Работа с интерфейсом Программист 1С:Предприятие 8 Бесплатно (free)

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

14.05.2025    6261    DeerCven    15    

57

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

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

21.05.2024    48483    dimanich70    83    

169

Универсальные функции Программист 1С:Предприятие 8 1C:Бухгалтерия Абонемент ($m)

Задача: вставить картинку из буфера обмена на форму средствами платформы 1С.

1 стартмани

18.03.2024    7275    6    John_d    13    

59

Универсальные функции Программист Стажер 1С:Предприятие 8 1C:Бухгалтерия Бесплатно (free)

Пришлось помучиться с GUID-ами немного, решил поделиться опытом, мало ли кому пригодится.

12.02.2024    60577    atdonya    31    

69

Универсальные функции Программист 1С:Предприятие 8 Бесплатно (free)

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

30.11.2023    9054    ke.92@mail.ru    17    

68
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Avt_Biz 25.07.18 22:25 Сейчас в теме
На Android не работает ! Требует установить расширение для работы с файлами, если установить расширение через код то идет ошибка об использовании модальных окон.
2. lenarha 9 03.08.18 19:40 Сейчас в теме
что в себе содержит процедура НачатьОтсчетШагов(ПараметрыСценария); Если ЭтотШагЕщеНеВыполнялся(ПараметрыСценария) Тогда

из текста не понятна логика
Igor253; shamahn; moemoe; sergey512; wowik; Gendelf; moonchild1; lonedog; AndreykO; +9 Ответить
3. user1063327 11.12.19 12:17 Сейчас в теме
То чувство, когда статья про отказ от модальности, но в коде используют вызов модальной процедуры ПоказатьВопрос() ПоказатьВопрос(Оповещение, ТекстСообщения, РежимДиалогаВопрос.ДаНет);
Igor253; 1c_ssnik; +2 Ответить
6. sinkawa 09.03.24 18:35 Сейчас в теме
(3) Это немодальный аналог модального метода глобального контекста "Вопрос".
Модальные методы не содержат в себе параметр типа "ОписаниеОповещения", поскольку приостанавливают выполнения кода до момента завершения своей работы.

Также выдержка из описания метода "Вопрос" в Синтакс-помощнике:
Если для конфигурации свойство РежимИспользованияМодальности установлено в НеИспользовать, следует использовать метод ПоказатьВопрос.
4. Aleksandr_prof 207 11.06.21 12:27 Сейчас в теме
А можете объяснить, в чём тут асинхронность и для чего она нужна в нашем случае?
1c_ssnik; +1 Ответить
5. CaSH_2004 375 31.07.21 23:25 Сейчас в теме
Описанный пример не рабочий т.к. код приведен не полностью. Отсутствуют:
1. НачатьОтсчетШагов()
2. ЭтотШагЕщеНеВыполнялся()
dooD1iez; Igor253; @lexandr; 1c_ssnik; +4 Ответить
7. sinkawa 09.03.24 18:38 Сейчас в теме
(5) Автор статьи в тексте оставил ссылку на оригинальную статью, по которой писал свой метод "ВыполнитьСценарийЗагрузкиФайлаНаСервер". Там описаны эти методы.
Для отправки сообщения требуется регистрация/авторизация