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