Работа с внешними файлами, такими как Microsoft Excel, в тонком клиенте 1С имеет свои архитектурные особенности. Основная сложность заключается в том, что в тонком клиенте мы ограничены в использовании объектов, которые привычны для толстого клиента (например, прямое использование COM-объектов без установленного ПО на стороне пользователя). Кроме того, переменные, объявленные в процедурах, не сохраняют свое состояние между вызовами сервера, если они не являются реквизитами формы.
В этой статье мы разберем, как правильно организовать чтение Excel-файла, где хранить полученные массивы данных и как обеспечить пользователю удобный визуальный выбор колонок для загрузки в справочники.
Рассмотрим ситуацию: вы прочитали данные из Excel в процедуре &НаКлиенте. Как только выполнение процедуры завершается, локальные переменные (массивы, структуры) уничтожаются. Если вы хотите, чтобы данные были доступны при последующих действиях пользователя (например, при нажатии кнопки «Загрузить» после выбора колонок), вам необходимо сохранить их в контексте формы.
Важный нюанс: Объект ТаблицаЗначений недоступен на тонком клиенте. Вместо него используются ДанныеФормыКоллекция, которые связаны с реквизитами формы. Однако передача всей таблицы данных Excel через контекст формы может значительно замедлить работу системы, так как эти данные будут пересылаться между клиентом и сервером при каждом вызове.
Один из самых эффективных способов — использование реквизита формы с типом ТабличныйДокумент. Этот объект обладает рядом преимуществ:
.xlsx напрямую без использования COM-объектов и установленного Excel.Разберем, как реализовать чтение файла в ТабличныйДокумент:
&НаКлиенте
Процедура ПрочитатьФайлExcel(ПутьКФайлу)
// Очищаем текущий реквизит формы (тип ТабличныйДокумент)
Объект.ДанныеЭксель.Очистить();
// Читаем файл. Используем способ чтения "Текст", чтобы избежать проблем с типами
Объект.ДанныеЭксель.Прочитать(ПутьКФайлу, СпособЧтенияЗначенийТабличногоДокумента.Текст);
// Теперь данные доступны в реквизите формы и их можно анализировать
Высота = Объект.ДанныеЭксель.ВысотаТаблицы;
Ширина = Объект.ДанныеЭксель.ШиринаТаблицы;
Сообщить("Прочитано строк: " + Высота + ", колонок: " + Ширина);
КонецПроцедуры
Проанализируем ситуацию, упомянутую в обсуждении: иногда 1С считывает гораздо больше колонок, чем реально заполнено данными (например, 252 колонки). Это происходит из-за форматирования пустых ячеек в самом Excel. В таком случае нам потребуется программно определить реальные границы данных, перебирая ячейки с конца таблицы.
Чтобы пользователь мог выбрать нужную колонку, мы можем предложить ему визуальный интерфейс. Например, вывести первые 15 строк в таблицу на форме (превью). Для получения данных из ТабличныйДокумент используем метод Область().
Рассмотрим пример получения значений из конкретной колонки для заполнения списка выбора:
&НаКлиенте
Процедура ПолучитьПревьюДанных(НомерКолонки)
СписокПревью.Очистить();
// Ограничим выборку первыми 15 строками для быстродействия
КоличествоСтрок = Мин(Объект.ДанныеЭксель.ВысотаТаблицы, 15);
Для НомСтр = 1 По КоличествоСтрок Цикл
// Обращаемся к ячейке по координатам (строка, колонка)
ТекстЯчейки = Объект.ДанныеЭксель.Область(НомСтр, НомКолонки).Текст;
СписокПревью.Добавить(ТекстЯчейки);
КонецЦикла;
КонецПроцедуры
Этот метод позволяет работать с данными на клиенте без постоянных обращений к серверу, так как содержимое реквизита ТабличныйДокумент уже находится в контексте формы.
Если файл Excel содержит десятки тысяч строк, хранить его целиком в реквизите формы — плохая практика. Контекст формы станет «тяжелым», и пользователь почувствует задержки (фризы). В этом случае выясним причину медленной работы и применим Временное хранилище.
Алгоритм действий следующий:
ПоместитьФайл().ТаблицуЗначений).При использовании метода Прочитать() для табличного документа важно помнить о параметре СпособЧтенияЗначенийТабличногоДокумента. Если вы выберете Значение, 1С попытается преобразовать данные в типы платформы (Число, Дата). Однако в Excel данные часто хранятся в «грязном» виде (пробелы в числах, разные форматы дат). Поэтому зачастую надежнее использовать Текст, а приведение типов выполнять самостоятельно программно.
Посмотрим на пример обработки "грязных" данных:
Функция ПреобразоватьВЧисло(ЗначениеИзЭксель)
// Удаляем неразрывные пробелы и прочий мусор
ЧистаяСтрока = СтрЗаменить(ЗначениеИзЭксель, Символы.НПП, "");
ЧистаяСтрока = СтрЗаменить(ЧистаяСтрока, " ", "");
Попытка
Возврат Число(ЧистаяСтрока);
Исключение
Возврат 0;
КонецПопытки;
КонецФункции
В сообщениях темы упоминается работа через ExcelЛист.Cells. Это использование COM-технологии. Разберем, почему в тонком клиенте это «монструозно», как выразился автор темы:
Cells(x, y) через COM-мост требует времени. Чтение 1000 строк может занять минуты, тогда как ТабличныйДокумент.Прочитать() сделает это за секунды.Таким образом, для тонкого клиента оптимальным является использование встроенных средств платформы для чтения файлов и временного хранилища для передачи данных между уровнями приложения.