Как правильно прочитать и загрузить CSV-файл в табличную часть документа в 1С 8.3?

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

При разработке механизмов импорта данных из внешних файлов в управляемом приложении разработчики часто сталкиваются с ошибками контекста выполнения. Типичные примеры таких ошибок: «Метод объекта не обнаружен (ПолучитьСтроку)» или невозможность создать объект ТаблицаЗначений в коде с директивой &НаКлиенте. Проанализируем, почему возникают эти проблемы, как устроено правильное взаимодействие между клиентом и сервером при загрузке файлов, и рассмотрим надежные варианты реализации чтения формата CSV.

Причины ошибок при чтении файлов на клиенте

Выясним причину, из-за которой код завершается аварийно. В клиент-серверной архитектуре платформы 1С:Предприятие 8.3 действуют строгие ограничения на доступность типов данных и методов:

  1. Недоступность серверных типов на клиенте: Объект ТаблицаЗначений не существует в контексте тонкого клиента и веб-клиента. Попытка выполнить конструкцию Новый ТаблицаЗначений на клиенте вызывает исключение.
  2. Различия в объектах для работы с текстом: Объект ТекстовыйДокумент в управляемых формах имеет ограниченный функционал на клиенте, а чтение больших файлов целиком в память через этот объект замедляет работу системы. Для потокового чтения текста предназначен объект ЧтениеТекста или специализированный объект ЧтениеCSV.
  3. Ограничения доступа к файловой системе: Сервер 1С не имеет прямого доступа к локальным дискам пользователя (например, к пути C:\Users\...). Поэтому файл с клиента необходимо сначала передать во временное хранилище и только затем обрабатывать на сервере.

Шаг 1. Передача файла с клиента на сервер через временное хранилище

Разберем по шагам базовый и наиболее универсальный алгоритм. Пользователь интерактивно выбирает файл на своем компьютере, после чего платформа отправляет его в оперативное временное хранилище. Для этого воспользуемся асинхронной процедурой НачатьПомещениеФайлаНаСервер (или НачатьПомещениеФайла).

Посмотрим на пример клиентского кода:


&НаКлиенте
Процедура ЗагрузитьCSV(Команда)
    Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
    
    // Интерактивный вызов диалога и отправка файла в хранилище за одно действие
    НачатьПомещениеФайлаНаСервер(
        Оповещение,
        , // Адрес в хранилище (будет создан автоматически)
        , // Диалог открытия файла будет открыт платформой
        , // Параметры диалога
        ЭтаФорма.УникальныйИдентификатор
    );
КонецПроцедуры

&НаКлиенте
Процедура ПослеПомещенияФайла(ОписаниеФайла, ДополнительныеПараметры) Экспорт
    Если ОписаниеФайла = Неопределено Тогда
        Возврат;
    КонецЕсли;
    
    // Передаем полученный адрес во временном хранилище на сервер
    ПрочитатьИЗаполнитьНаСервере(ОписаниеФайла.Адрес);
КонецПроцедуры

Шаг 2. Чтение данных на сервере с использованием специализированного объекта ЧтениеCSV

Рассмотрим подробнее обработку полученного файла на сервере. Начиная с версии платформы 8.3.6, для разбора таких файлов рекомендуется использовать объект ЧтениеCSV. В отличие от простого построчного разбиения через СтрРазделить или самописные функции, ЧтениеCSV корректно обрабатывает экранированные кавычки, многострочный текст внутри колонок и различные разделители.

Чтобы не сохранять промежуточные файлы на диск сервера и не зависеть от операционной системы (Windows/Linux) и прав доступа к папкам, прочитаем данные напрямую из потока в оперативной памяти.

Разберем пример серверной процедуры:


&НаСервере
Процедура ПрочитатьИЗаполнитьНаСервере(АдресВХранилище)
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
    Если ДвоичныеДанные = Неопределено Тогда
        ВызватьИсключение "Не удалось получить файл из временного хранилища.";
    КонецЕсли;
    
    // Открываем поток для чтения без сохранения на жесткий диск
    Поток = ДвоичныеДанные.ОткрытьПотокДляЧтения();
    
    Чтение = Новый ЧтениеCSV;
    // Настраиваем параметры: разделитель полей - точка с запятой
    ПараметрыCSV = Новый ПараметрыЧтенияCSV;
    ПараметрыCSV.РазделительПолей = ";";
    ПараметрыCSV.СимволМаскировки = """";
    
    Чтение.ОткрытьПоток(Поток, ПараметрыCSV);
    
    // Читаем заголовок (первую строку)
    МассивКолонок = Чтение.Прочитать();
    Если МассивКолонок = Неопределено Тогда
        Чтение.Закрыть();
        Возврат;
    КонецЕсли;
    
    // Очищаем табличную часть перед заполнением
    Объект.ДанныеЗаМесяц.Очистить();
    
    // Последовательно читаем строки файла
    Пока Истина Цикл
        СтрокаДанных = Чтение.Прочитать();
        Если СтрокаДанных = Неопределено Тогда
            Прервать; // Достигнут конец файла
        КонецЕсли;
        
        // Заполняем строку табличной части документа
        НоваяСтрока = Объект.ДанныеЗаМесяц.Добавить();
        
        // Пример заполнения реквизитов по индексам колонок CSV
        Если СтрокаДанных.Количество() > 0 Тогда
            НоваяСтрока.Артикул = СокрЛП(СтрокаДанных[0]);
        КонецЕсли;
        
        Если СтрокаДанных.Количество() > 1 Тогда
            НоваяСтрока.Количество = Число(СтрокаДанных[1]);
        КонецЕсли;
    КонецЦикла;
    
    Чтение.Закрыть();
КонецПроцедуры

Альтернативный вариант: чтение через ЧтениеТекста

Если структура файла простая и гарантированно не содержит переносов строк внутри полей, можно использовать объект ЧтениеТекста в связке с системной функцией СтрРазделить. Посмотрим, как это реализуется с сохранением во временный файл:


&НаСервере
Процедура ПрочитатьЧерезЧтениеТекста(АдресВХранилище)
    ДвоичныеДанные = ПолучитьИзВременногоХранилища(АдресВХранилище);
    
    // Получаем уникальное имя временного файла в каталоге сервера
    ИмяВременногоФайла = ПолучитьИмяВременногоФайла("csv");
    ДвоичныеДанные.Записать(ИмяВременногоФайла);
    
    Чтение = Новый ЧтениеТекста(ИмяВременногоФайла, КодировкаТекста.UTF8);
    
    Попытка
        // Пропускаем строку заголовков
        Заголовок = Чтение.ПрочитатьСтроку();
        
        Пока Истина Цикл
            ТекстСтроки = Чтение.ПрочитатьСтроку();
            Если ТекстСтроки = Неопределено Тогда
                Прервать;
            КонецЕсли;
            
            // Встроенная функция платформы для разделения строки
            МассивЗначений = СтрРазделить(ТекстСтроки, ";", Истина);
            
            Если МассивЗначений.Количество() >= 2 Тогда
                НоваяСтрока = Объект.ДанныеЗаМесяц.Добавить();
                НоваяСтрока.НоменклатураНаименование = СокрЛП(МассивЗначений[0]);
                НоваяСтрока.Сумма = Число(МассивЗначений[1]);
            КонецЕсли;
        КонецЦикла;
    Фиксировать
    Исключение
        Чтение.Закрыть();
        УдалитьФайлы(ИмяВременногоФайла);
        ВызватьИсключение ОписаниеОшибки();
    КонецПопытки;
    
    // Обязательно закрываем дескриптор файла и удаляем временный файл
    Чтение.Закрыть();
    УдалитьФайлы(ИмяВременногоФайла);
КонецПроцедуры

Важные правила при работе с файлами и контекстом формы

  1. Не обращайтесь к элементам формы для изменения данных: Конструкция Элементы.ИмяТаблицы.ДобавитьСтроку() предназначена только для интерактивного управления интерфейсом. На сервере заполнение данных документа выполняется исключительно через реквизит данных: Объект.ИмяТабличнойЧасти.Добавить().
  2. Освобождайте ресурсы: Всегда закрывайте объекты чтения (ЧтениеТекста.Закрыть() или ЧтениеCSV.Закрыть()), чтобы не блокировать файлы процессами сервера.
  3. Не используйте жестко заданные пути: Использование путей вроде C:\temp\file.txt приводит к сбоям прав доступа и ошибкам на серверах под управлением Linux. Применяйте потоки из ДвоичныеДанные либо системную функцию ПолучитьИмяВременногоФайла().
← На главную