Загрузка и обработка большого файла EXCEL в 1С с прогрессбаром
Задача: «фоново» загрузить файл эксель большого размера, с показом прогресса загрузки.
Решение:
В ходе выполнения задачи столкнулся с глюком платформы 1С, что при запуске фонового задания с параметром адреса временного хранилища файла, он приходит на сервер пустым. Потому пришлось делать «финт ушами», а именно перед запуском фонового задания, файл загрузить на сервер, получить его временное имя и уже затем передать его как параметр при запуске фонового задания.
Кроме того выплыла проблема с не рабочим способом передачи данных о загрузке при помощи хранения данных во «ВременныхХранилиах», описанным тут. Потому для получения прогресса воспользуемся возможностью зная идентификатор фонового процесса периодически получить с сервера данные выводимые при помощи «Сообщить()».
В общих модулях разместим следующий код фонового процесса:
&НаСервере
Функция ЗагрузкаФайлаПредварительная(АдресВременногоХранилищаФайла) Экспорт
ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВременногоХранилищаФайла);
ИмяВременногоФайлаСпр = ПолучитьИмяВременногоФайла("xlsx");
ДвоичныеДанные.Записать(ИмяВременногоФайлаСпр);
возврат ИмяВременногоФайлаСпр;
конецфункции
Процедура ЗагрузкаФайлаДлительная(ИмяВременногоФайлаСпр) Экспорт
ТабличныйДокументСпр = Новый ТабличныйДокумент;
ТабличныйДокументСпр.Прочитать(ИмяВременногоФайлаСпр,СпособЧтенияЗначенийТабличногоДокумента.Значение);
//читаем все листы файла СправочникСМС
Для Каждого ОбластьТД ИЗ ТабличныйДокументСпр.Области Цикл
ОбластьФайла = ТабличныйДокументСпр.ПолучитьОбласть(ОбластьТД.Имя);
КолВоСтрокФайла = ОбластьФайла.ПолучитьРазмерОбластиДанныхПоВертикали();
КолВоКолонокФайла = ОбластьФайла.ПолучитьРазмерОбластиДанныхПоГоризонтали();
Сообщить("Строк:"+КолВоСтрокФайла);
Сообщить("Колонок:"+КолВоКолонокФайла);
имялиста=ОбластьТД.Имя;
Сообщить("Лист:"+имялиста);
МассивИменКолонок=Новый Массив();
// читаем шапку листа
Для ит=1 ПО КолВоКолонокФайла Цикл
нКолонка = СтрЗаменить(ит, Символы.НПП, "");
ИмяКолонки=ОбластьФайла.ПолучитьОбласть("R3" + "C"+нКолонка).ТекущаяОбласть.Текст;
МассивИменКолонок.Добавить(ИмяКолонки);
конеццикла;
НачСтрока=3;КонСтрока=0;
НачСтрока = ?(НачСтрока = 0, 2, НачСтрока);
КонСтрока = ?(КонСтрока = 0, КолвоСтрокФайла, КонСтрока);
//перебираем все строки без первой строки
Индикатор=0;
Для нСтрокаТФ = НачСтрока+1 ПО КонСтрока Цикл
нСтрока = СтрЗаменить(нСтрокаТФ, Символы.НПП, "");
если Окр(нСтрокаТФ*100/КонСтрока)<>Индикатор тогда
Индикатор = Окр(нСтрокаТФ*100/КонСтрока);
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Выполнено %"+Индикатор;
Сообщение.Сообщить();
конецесли;
Для нКолонкаТФ = 1 ПО КолВоКолонокФайла Цикл
нКолонка = СтрЗаменить(нКолонкаТФ, Символы.НПП, "");
Область = ОбластьФайла.ПолучитьОбласть("R"+нСтрока+"C"+нКолонка);
ТекущаяОбласть = Область.ТекущаяОбласть;
ЗначениеЯчейки = СокрЛП(ТекущаяОбласть.Текст);
...
обрабатываем файл
...
конеццикла;
конеццикла;
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Загрузка завершена.";
Сообщение.Сообщить();
Клиентская часть в управляемых формах:
&НаКлиенте
Процедура Жмяк(Команда)
Режим = РежимДиалогаВыбораФайла.Открытие;
ДиалогОткрытияФайла = Новый ДиалогВыбораФайла(Режим);
ДиалогОткрытияФайла.ПолноеИмяФайла = "";
Фильтр = НСтр("ru = 'Текст'; en = 'Text'")+ "(*.xls)|*.xlsx";
ДиалогОткрытияФайла.Фильтр = Фильтр;
ДиалогОткрытияФайла.МножественныйВыбор = ложь;
ДиалогОткрытияФайла.Заголовок = "Выберите файлы";
Если ДиалогОткрытияФайла.Выбрать() Тогда
МассивФайлов = ДиалогОткрытияФайла.ВыбранныеФайлы;
ФайлСМС="";
Для Каждого ИмяФайла Из МассивФайлов Цикл
ФайлСМС=ИмяФайла;
конеццикла;
если ФайлСМС<>"" тогда
АдресХранилищаФайла = "";
Состояние("Перемещаю файл на сервер");
ПоместитьФайл(АдресХранилищаФайла, ФайлСМС, , Ложь, ЭтаФорма.УникальныйИдентификатор);
Состояние("Обрабатывается файл "+ФайлСМС);
ЗапускФоновойЗагрузкиСправочника(АдресХранилищаФайла);
Состояние("Запущена фоновая обработка файла");
ПодключитьОбработчикОжидания("ИндикаторВыполненияЗагрузки",1,ложь);
иначе
сообщить("Файл не выбран");
конецесли;
конецесли;
КонецПроцедуры
&НаСервере
Процедура ЗапускФоновойЗагрузкиСправочника(АдресХранилищаФайла)
ИмяВременногоФайлаСпр=СК_Регламентные.ЗагрузкаФайлаПредварительная(АдресХранилищаФайла);
МассивПараметров = Новый Массив;
МассивПараметров.Добавить(ИмяВременногоФайлаСпр);
Сообщить("Адрес файла (1):"+АдресХранилищаФайла);
ФЗ = ФоновыеЗадания.Выполнить("СК_Регламентные.ЗагрузкаФайлаДлительная",МассивПараметров);
ЭтаФорма.ФоновоеИдентификатор = ФЗ.УникальныйИдентификатор;
КонецПроцедуры
&НаСервере
Функция ПолучитьСообщенияФЗ(ФЗ, Состояние = Неопределено, УдалятьСообщения = Ложь) Экспорт
Если Состояние = Неопределено Тогда
Состояние = ФЗ.Состояние;
КонецЕсли;
МассивСообщений = Новый Массив;
Сообщения = ФЗ.ПолучитьСообщенияПользователю(УдалятьСообщения);
Если Сообщения <> Неопределено Тогда
Для Каждого Сообщение Из Сообщения Цикл
МассивСообщений.Добавить(Сообщение.Текст);
КонецЦикла;
КонецЕсли;
Возврат МассивСообщений;
КонецФункции
&НаСервере
Функция ОпроситьФоновые()
прог=неопределено;
ФЗ = ФоновыеЗадания.НайтиПоУникальномуИдентификатору(ЭтаФорма.ФоновоеИдентификатор);
ФСообщения=ПолучитьСообщенияФЗ(ФЗ,,истина);
Если ФСообщения.Количество() > 0 Тогда
Для Каждого Сообщение Из ФСообщения Цикл
Сообщить(Сообщение);
если найти(Сообщение,"%")>0 тогда
Этаформа.Прогресс=СтрЗаменить(Сообщение,"Выполнено %","");
прог=Этаформа.Прогресс;
конецесли;
КонецЦикла;
КонецЕсли;
возврат прог;
КонецФункции
&НаКлиенте
Процедура ИндикаторВыполненияЗагрузки() Экспорт
пр=ОпроситьФоновые();
если пр<>неопределено тогда
Состояние("Выполнено "+пр);
конецесли;
КонецПроцедуры