Как правильно загрузить Excel-файл в Табличный Документ на сервере и обработать данные в 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Зарплата и Управление Персоналом IT и автоматизация бизнеса
← На главную

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

Постановка задачи и основная проблема

Часто возникает ситуация, когда необходимо создать обработку, где пользователь выбирает файл Excel (xls или xlsx) на своем компьютере, а система должна прочитать этот файл и вывести данные в табличный документ или загрузить их в справочники/документы. Для этой задачи есть универсальный загрузчик данных Excel для 1С.

В нашем случае мы сталкиваемся с ошибкой: "Ошибка при вызове метода контекста (Прочитать). Ошибка доступа к выбранному файлу" или же табличный документ просто остается пустым. При этом путь к файлу выбран корректно.

Давайте разберем, почему так происходит. В управляемом приложении код разделен на исполняемый &НаКлиенте (на компьютере пользователя) и &НаСервере (где работает сервер 1С). Когда пользователь выбирает файл через диалоговое окно, он получает путь к файлу на своем локальном диске (например, C:\Users\User\Desktop\Report.xls). Если мы передадим этот строковый путь на сервер и попытаемся там выполнить метод чтения, сервер будет искать этот путь на своем локальном диске, где такого файла, разумеется, нет.

Решение проблемы доступа к файлу: Временное хранилище

Чтобы сервер "увидел" файл, нам необходимо физически передать данные файла с клиента на сервер. В 1С для этого используется механизм Временного хранилища. Для автоматизации этого процесса подойдёт загрузка документов из Excel с распознаванием структуры.

Рассмотрим правильный алгоритм действий:

  1. Пользователь выбирает файл на Клиенте.
  2. Мы помещаем этот файл во Временное хранилище (получаем уникальный адрес-ссылку).
  3. Передаем этот адрес на Сервер.
  4. На Сервере получаем данные из хранилища по адресу и сохраняем их во временный файл уже на диске сервера.
  5. Читаем этот временный файл.

Давайте напишем корректный код выбора файла и передачи его на сервер.

Шаг 1. Выбор файла и помещение в хранилище (На Клиенте)

Вместо того чтобы просто запоминать путь, нам нужно сразу получить двоичные данные файла. Используем объект ДиалогВыбораФайла.


&НаКлиенте
Процедура ПутьКФайлуНачалоВыбора(Элемент, ДанныеВыбора, СтандартнаяОбработка)
    
    СтандартнаяОбработка = Ложь;
    Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
    Диалог.Заголовок = "Выбор файла Excel";
    Диалог.Фильтр = "Excel файлы (*.xls;*.xlsx)|*.xls;*.xlsx";
    Диалог.МножественныйВыбор = Ложь;
    
    // Если файл выбран
    Если Диалог.Выбрать() Тогда
        // Сохраняем путь для отображения пользователю (визуально)
        Объект.ПутьКФайлу = Диалог.ПолноеИмяФайла;
    КонецЕсли;

КонецПроцедуры

Теперь рассмотрим процедуру, которая запускается по кнопке "Загрузить". Здесь мы помещаем файл в хранилище.


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

Чтение и обработка на Сервере

Теперь перейдем к серверной части. Нам нужно получить файл из хранилища, сохранить его во временную папку сервера и прочитать методом ТабличныйДокумент.Прочитать.


&НаСервере
Функция ПрочитатьФайлНаСервере(АдресДанных)
    
    ТабДок = Новый ТабличныйДокумент;
    
    // Получаем данные из хранилища
    ДанныеФайла = ПолучитьИзВременногоХранилища(АдресДанных);
    
    // Создаем имя для временного файла на сервере с правильным расширением
    ПутьКФайлуНаСервере = ПолучитьИмяВременногоФайла("xls");
    
    // Записываем данные на диск сервера
    ДанныеФайла.Записать(ПутьКФайлуНаСервере);
    
    Попытка
        // Читаем Excel в табличный документ 1С
        ТабДок.Прочитать(ПутьКФайлуНаСервере, СпособЧтенияЗначенийТабличногоДокумента.Значение);
    Исключение
        // Обязательно выводим понятное описание ошибки
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Ошибка чтения файла: " + ОписаниеОшибки();
        Сообщение.Сообщить();
        Возврат Неопределено;
    КонецПопытки;
    
    // Удаляем временный файл, чтобы не засорять диск сервера
    УдалитьФайлы(ПутьКФайлуНаСервере);
    
    // Возвращаем готовый табличный документ на клиент
    Возврат ТабДок;
    
КонецФункции

Разбор логических ошибок при обработкой данных

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

Ошибка 1. Синтаксис метода НайтиПоРеквизиту

В коде встречалась строка:


// ОШИБКА
СтрокаТаблица.Организация = Справочники.Организации.НайтиПоРеквизиту("ГоловнаяОрганизация");

Метод НайтиПоРеквизиту обязательно требует второго параметра — значения, которое мы ищем. Если мы хотим найти организацию, у которой реквизит "ГоловнаяОрганизация" равен Истина (предполагая, что это Булево), код должен выглядеть так:


// ПРАВИЛЬНО (если ищем булево значение)
СтрокаТаблица.Организация = Справочники.Организации.НайтиПоРеквизиту("ГоловнаяОрганизация", Истина);

Если же "ГоловнаяОрганизация" — это ссылка на другую организацию, то поиск должен осуществляться по другому принципу. Без второго параметра код вызовет ошибку выполнения.

Ошибка 2. Использование Построителя Отчета

В примере пользователя данные сначала считывались в ТаблицаЗначений, а затем использовался ПостроительОтчета:


ПостроительОтчета = Новый ПостроительОтчета; 
ПостроительОтчета.ИсточникДанных = Новый ОписаниеИсточникаДанных(ДанныеФайла);
ПостроительОтчета.Вывести(ТабДок); // Ошибка логики

Проблема здесь в том, что метод Вывести() у построителя отчета полностью очищает переданный ему табличный документ и строит в нем новый отчет на основе источника данных. Если ваша цель — показать пользователю исходный файл Excel, который вы только что прочитали через ТабДок.Прочитать(), то использование построителя отчета сотрет все прочитанные данные и нарисует простую таблицу.

Если же цель — сформировать реестр документов, то использование построителя оправдано, но тогда нет смысла возвращать исходный ТабДок.

Ошибка 3. Адресация ячеек (R1C1) и форматирование чисел

При обращении к ячейкам табличного документа по адресу R1C1 (строка, колонка) важно правильно форматировать номера строк.


// Потенциальная ошибка
Адрес = "R" + НомерСтроки + "C" + 2; 

В 1С числовое представление по умолчанию включает неразрывные пробелы для разделения триад (групп разрядов). Если НомерСтроки станет больше 999 (например, 1000), строка превратится в "1 000". Адрес ячейки станет "R1 000C2", что является некорректным синтаксисом и вызовет ошибку.

Правильный подход: всегда использовать функцию Формат с отключением группировки:


НомерСтрокиБезПробелов = Формат(НомерСтроки, "ЧГ=0");
Адрес = "R" + НомерСтрокиБезПробелов + "C" + 2;
Ячейка = ТабДок.Область(Адрес);

Полный пример обработки данных (извлечение в Таблицу Значений)

Если наша задача не просто показать Excel, а программно разобрать данные, то алгоритм будет следующим:


&НаСервере
Функция РазобратьФайлВТаблицу(АдресДанных)
    
    // 1. Получаем файл и читаем в ТабДок (как описано выше)
    ТабДок = Новый ТабличныйДокумент;
    // ... (код получения файла и чтения, см. выше) ...
    
    // 2. Подготавливаем таблицу для результата
    ДанныеФайла = Новый ТаблицаЗначений;
    ДанныеФайла.Колонки.Добавить("Сотрудник", Новый ОписаниеТипов("Строка"));
    ДанныеФайла.Колонки.Добавить("Сумма", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15, 2)));
    
    // 3. Обходим строки
    ВысотаТаблицы = ТабДок.ВысотаТаблицы;
    
    Для НомерСтр = 2 По ВысотаТаблицы Цикл
        
        // Формируем формат номера строки без пробелов
        СтрИндекс = Формат(НомерСтр, "ЧГ=0");
        
        // Получаем значение из 2-й колонки (допустим, ФИО)
        ТекстСотрудник = СокрЛП(ТабДок.Область("R" + СтрИндекс + "C2").Текст);
        
        // Получаем значение из 5-й колонки (Сумма)
        ТекстСумма = СокрЛП(ТабДок.Область("R" + СтрИндекс + "C5").Текст);
        
        // Если нашли ключевое слово (как в примере автора)
        ЧетвертаяКолонка = СокрЛП(ТабДок.Область("R" + СтрИндекс + "C4").Текст);
        
        Если ЗначениеЗаполнено(ТекстСотрудник) Тогда
             НоваяСтрока = ДанныеФайла.Добавить();
             НоваяСтрока.Сотрудник = ТекстСотрудник;
             
             // Преобразуем сумму. В Excel могут быть неразрывные пробелы
             ТекстСумма = СтрЗаменить(ТекстСумма, Символы.НПП, ""); 
             ТекстСумма = СтрЗаменить(ТекстСумма, " ", "");
             
             Попытка
                 НоваяСтрока.Сумма = Число(ТекстСумма);
             Исключение
                 НоваяСтрока.Сумма = 0;
             КонецПопытки;
        КонецЕсли;
        
    КонецЦикла;
    
    Возврат ДанныеФайла;

КонецФункции

Заключение

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

← На главную