Как правильно обработать большой файл в 1С 8.3 и создать документы без зависания системы

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

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

Почему обработка большого файла на клиенте — это критическая ошибка

Давайте подробно разберем, какие физические процессы происходят в платформе 1С при попытке разобрать крупный файл на клиенте:

  1. Блокировка интерфейса (зависание программы): Любой ресурсоемкий цикл на клиенте монополизирует поток выполнения. Поскольку тонкий клиент работает в одном основном потоке, интерфейс 1С зависнет, операционная система присвоит статус "Программа не отвечает", а пользователь может принудительно закрыть приложение, прервав процесс на середине.
  2. Превышение лимитов памяти: Тонкий клиент часто функционирует в 32-разрядном адресном пространстве, где доступный объем оперативной памяти жестко ограничен. Чтение мегабайтного текстового файла с созданием тысяч промежуточных объектов типа Структура быстро переполнит выделенную память, вызвав аварийное закрытие процесса.
  3. Избыточный сетевой трафик: Желание автора сэкономить время на передаче файла на сервер ошибочно. Передача исходного бинарного файла через временное хранилище происходит крайне быстро в один поток. Напротив, если распарсить файл на клиенте в массив структур и передать его на сервер, объем сериализованных данных в XML или JSON увеличится в несколько раз, что вызовет колоссальные задержки при передаче по сети.

Как временно накопить данные на клиенте (для небольших файлов)

Если объем данных действительно невелик (до нескольких сотен строк) и вы абсолютно уверены в стабильности клиентской машины, можно воспользоваться накоплением данных в памяти клиента. Поскольку объект ТаблицаЗначений в управляемом приложении на клиенте недоступен, оптимальным решением является использование массива структур.

Давайте посмотрим на практический пример создания такой структуры:


МассивРезультат = Новый Массив;
// Обрабатываем строки во временном цикле
Для Счетчик = 1 По 100 Цикл
    НоваяСтрока = Новый Структура;
    НоваяСтрока.Вставить("Дата", ТекущаяДата());
    НоваяСтрока.Вставить("Действие", "прыг");
    НоваяСтрока.Вставить("Тип", "сильно");
    НоваяСтрока.Вставить("Количество", Счетчик);
    НоваяСтрока.Вставить("ВремяОкончания", "04:12");
    
    МассивРезультат.Добавить(НоваяСтрока);
КонецЦикла;

Полученный МассивРезультат можно легко передать в качестве параметра на сервер. Однако, если количество строк измеряется тысячами, забудьте про этот метод и используйте исключительно серверную обработку.

Правильная архитектура решения: Клиент-Серверный паттерн

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

Этап 1. Асинхронное помещение файла во временное хранилище

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

Разберем, как правильно реализовать интерактивный выбор файла на клиенте и его отправку на сервер:


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

В результате вызова этой функции платформа откроет стандартный диалог выбора файла. После выбора файл будет передан на сервер, а управление перейдет к процедуре обратного вызова:


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

Этап 2. Потоковое чтение данных на сервере

Когда файл загружен на сервер, нам необходимо прочитать его содержимое. Распространенная ошибка на этом этапе — использование объекта ТекстовыйДокумент. Метод Прочитать() загружает весь файл целиком в оперативную память сервера, что при больших объемах (от 50 Мб) вызывает резкий скачок потребления ОЗУ процессом rphost.

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

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


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

Этап 3. Использование фоновых заданий для длительных процессов

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

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

← На главную