При разработке механизмов загрузки данных из внешних файлов (Excel, CSV, TXT) начинающие программисты часто сталкиваются с ситуацией, когда код вроде бы написан верно, но результат выполнения запроса остается пустым. В этой статье мы подробно разберем, почему возникает ошибка загрузки, как правильно передавать данные между клиентом и сервером и какими способами можно прочитать файл XLSX в среде 1С:Предприятие 8.3.
Рассмотрим ситуацию, представленную в обсуждении. Разработчик пытается выполнить запрос к временной таблице, источником которой является Реквизит1.Выгрузить(). Основная логическая ошибка заключается в том, что на момент вызова метода УстановитьПараметр() таблица на форме еще не содержит данных из файла. Мы должны четко понимать: запрос в 1С не умеет самостоятельно ходить во внешние файлы. Он работает только с теми данными, которые уже находятся в оперативной памяти (в виде таблиц значений) или в базе данных.
Если в таблице-источнике ноль строк, то и результат запроса всегда будет содержать ноль строк. Прежде чем обращаться к данным через Запрос, их необходимо физически прочитать из файла и поместить в объект ТаблицаЗначений.
В управляемом приложении работа с файлами разделена: пользователь выбирает файл на своей стороне (клиент), а обработка данных происходит на стороне сервера. Проанализируем правильный алгоритм передачи файла:
ПоместитьФайлВоВременноеХранилище().Посмотрим на пример кода для выбора файла:
&НаКлиенте
Процедура ВыбратьИЗагрузитьФайл(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеВыбораФайла", ЭтотОбъект);
ПараметрыДиалога = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
ПараметрыДиалога.Фильтр = "Excel (*.xlsx)|*.xlsx";
ПараметрыДиалога.Показать(Оповещение);
КонецПроцедуры
&НаКлиенте
Процедура ПослеВыбораФайла(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Если ВыбранныеФайлы <> Неопределено Тогда
АдресХранилища = "";
// Помещаем файл во временное хранилище для доступа сервера
ПоместитьФайл(АдресХранилища, ВыбранныеФайлы[0], , Ложь);
ЗагрузитьДанныеНаСервере(АдресХранилища);
КонецЕсли;
КонецПроцедуры
Выясним, как прочитать данные, когда адрес файла уже получен. Самый надежный и современный способ, не требующий установки Microsoft Excel на сервере, — использование объекта ТабличныйДокумент.
Разберем по шагам процесс чтения:
&НаСервере
Процедура ЗагрузитьДанныеНаСервере(АдресХранилища)
// Извлекаем данные из хранилища
ДанныеФайла = ПолучитьИзВременногоХранилища(АдресХранилища);
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("xlsx");
ДанныеФайла.Записать(ИмяВременногоФайла);
// Читаем файл в табличный документ
ТабДок = Новый ТабличныйДокумент;
ТабДок.Прочитать(ИмяВременногоФайла);
// Создаем таблицу значений для хранения "сырых" данных
ТЗ_Данные = Новый ТаблицаЗначений;
ТЗ_Данные.Колонки.Добавить("ТоварНаименование", Новый ОписаниеТипов("Строка"));
ТЗ_Данные.Колонки.Добавить("Количество", Новый ОписаниеТипов("Число"));
// Обходим строки табличного документа (начиная со второй, если первая - заголовок)
Для НомерСтроки = 2 По ТабДок.ВысотаТаблицы Цикл
НоваяСтрока = ТЗ_Данные.Добавить();
НоваяСтрока.ТоварНаименование = ТабДок.Область(НомерСтроки, 1).Текст;
НоваяСтрока.Количество = Число(ТабДок.Область(НомерСтроки, 2).Текст);
КонецЦикла;
// Теперь ТЗ_Данные заполнена, и ее можно передавать в запрос
ОбработатьДанныеЗапросом(ТЗ_Данные);
КонецПроцедуры
Рассмотрим ситуацию, когда нам действительно нужен Запрос. Обычно это требуется для сопоставления текстовых названий из Excel со ссылками на справочники базы данных или для суммирования дублей. Проанализируем, как правильно передать заполненную таблицу в запрос.
Важный момент: для использования таблицы значений в запросе необходимо передать её как параметр и обязательно описать её структуру внутри текста запроса (типы колонок должны быть определены).
&НаСервере
Процедура ОбработатьДанныеЗапросом(ТЗ_Данные)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ДанныеИзФайла.ТоварНаименование КАК ТоварНаименование,
| ДанныеИзФайла.Количество КАК Количество
|ПОМЕСТИТЬ ВТ_Загрузка
|ИЗ
| &ТЗ_Данные КАК ДанныеИзФайла
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| ВТ_Загрузка.ТоварНаименование КАК Наименование,
| ЕСТЬNULL(Номенклатура.Ссылка, ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)) КАК СсылкаНаТовар,
| СУММА(ВТ_Загрузка.Количество) КАК ОбщееКоличество
|ИЗ
| ВТ_Загрузка КАК ВТ_Загрузка
| ЛЕВОЕ СОЕДИНЕНИЕ Справочник.Номенклатура КАК Номенклатура
| ПО ВТ_Загрузка.ТоварНаименование = Номенклатура.Наименование
|ГРУППИРОВАТЬ ПО
| ВТ_Загрузка.ТоварНаименование,
| Номенклатура.Ссылка";
Запрос.УстановитьПараметр("ТЗ_Данные", ТЗ_Данные);
Результат = Запрос.Выполнить();
ТаблицаРезультат = Результат.Выгрузить();
// Загружаем результат в реквизит формы (табличную часть)
Объект.Товары.Загрузить(ТаблицаРезультат);
КонецПроцедуры
Посмотрим на другие способы, упомянутые в ходе обсуждения:
Подведем итоги нашего исследования проблемы загрузки данных:
ТабличногоДокумента или через ПостроительЗапроса.C:\Users... на сервере может быть недоступен. Используйте временное хранилище.Число() или Дата().НайтиПоНаименованию или соединение в запросе), чтобы в системе хранились ссылки, а не просто строковые значения.Используя предложенные методики, вы сможете построить надежный и быстрый механизм импорта данных, который будет корректно работать в любых конфигурациях на базе платформы 1С:Предприятие 8.3.