Как правильно передавать большие объемы данных между клиентом и сервером в управляемых формах 1С?

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

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

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

Причина возникновения проблемы: Ограничения передачи данных

Когда мы выполняем серверный вызов из клиентского кода (или наоборот), платформа 1С осуществляет процесс сериализации передаваемых объектов на одной стороне и их десериализации на другой. Этот механизм позволяет обеспечить независимость клиентского и серверного кода и передавать данные по сети. Однако, для оптимизации работы и предотвращения чрезмерной нагрузки на сеть и сервер, платформа накладывает определенные ограничения на размер данных, которые могут быть переданы таким образом напрямую.

Если данные превышают установленный лимит (обычно это несколько мегабайт, хотя точное значение может варьироваться), платформа прерывает операцию и выдает ошибку. Это означает, что напрямую передать большую ТаблицуЗначений, содержащую тысячи строк и множество колонок, или объемный файл через параметры процедуры не получится. В таких случаях часто требуется специализированное копирование файлов и каталогов между Клиентом и Сервером или использование обходных путей.

Решение: Использование Временного Хранилища

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

Ключевыми функциями для работы с временным хранилищем являются:

  1. ПоместитьВоВременноеХранилище(): Эта функция используется для сохранения объекта во временном хранилище. В качестве результата она возвращает уникальную строку – адрес временного хранилища, по которому позже можно будет получить сохраненные данные.
  2. ПолучитьИзВременногоХранилища(): Эта функция позволяет получить объект из временного хранилища по его адресу.

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

Пошаговое руководство: Передача данных с клиента на сервер

Давайте подробно рассмотрим, как реализовать передачу большой ТаблицыЗначений с клиента на сервер, используя временное хранилище.

  1. Подготовка данных на клиенте.

    Прежде всего, у нас должны быть данные, которые мы хотим передать. Это может быть ТаблицаЗначений, СписокЗначений, ДвоичныеДанные или любой другой сериализуемый объект 1С. Для нашего примера предположим, что у нас есть заполненная ТаблицаЗначений под названием МояБольшаяТаблица.

    
    // Пример заполнения большой таблицы (имитация)
    МояБольшаяТаблица = Новый ТаблицаЗначений;
    МояБольшаяТаблица.Колонки.Добавить("Код");
    МояБольшаяТаблица.Колонки.Добавить("Наименование");
    МояБольшаяТаблица.Колонки.Добавить("Количество");
    
    Для Инд = 1 По 5000 Цикл
        НоваяСтрока = МояБольшаяТаблица.Добавить();
        НоваяСтрока.Код = "ЭЛ-" + Формат(Инд, "ЧЦ=5; ЧВН=;");
        НоваяСтрока.Наименование = "Элемент " + Инд + " для обработки";
        НоваяСтрока.Количество = Инд * 10;
    КонецЦикла;
    
  2. Помещение данных во временное хранилище на клиенте.

    Теперь нам нужно поместить МояБольшаяТаблица во временное хранилище. Это делается функцией ПоместитьВоВременноеХранилище(), которая возвращает адрес в виде строки. Этот адрес мы и будем передавать на сервер.

    
    // Помещаем таблицу во временное хранилище
    АдресДанныхВХранилище = ПоместитьВоВременноеХранилище(МояБольшаяТаблица);
    // АдресДанныхВХранилище теперь содержит строку типа "e1c8d197-0b13-43a7-b2f5-b2b7b3b4d6d6"
    

    Обратите внимание, что второй параметр функции ПоместитьВоВременноеХранилище() по умолчанию равен Ложь, что означает, что данные будут доступны только для текущего пользователя и текущей сессии. В большинстве случаев для клиент-серверного взаимодействия этого достаточно. Если же данные должны быть доступны для других пользователей или фоновых заданий, следует использовать Истина в качестве второго параметра.

  3. Вызов серверной процедуры с адресом.

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

    
    // Вызываем серверную функцию, передавая адрес данных
    СообщитьНаСервере(АдресДанныхВХранилище);
    

    Здесь СообщитьНаСервере – это пример серверной процедуры, которая должна быть определена в модуле формы или в общем модуле с директивой &НаСервере или &НаСервереБезКонтекста.

  4. Получение данных на сервере.

    В серверной процедуре мы используем функцию ПолучитьИзВременногоХранилища(), чтобы по полученному адресу восстановить наш объект (в нашем случае – ТаблицуЗначений).

    
    &НаСервере
    Процедура СообщитьНаСервере(Адрес)
        // Получаем таблицу значений из временного хранилища
        МояПолученнаяТаблица = ПолучитьИзВременногоХранилища(Адрес);
    
        Если МояПолученнаяТаблица <> Неопределено Тогда
            // Теперь мы можем работать с МояПолученнаяТаблица на сервере
            // Например, вывести количество строк:
            Сообщить("На сервере получено строк: " + МояПолученнаяТаблица.Количество());
            // Выполняем дальнейшую обработку данных
            // ...
        Иначе
            Сообщить("Не удалось получить данные по адресу: " + Адрес);
        КонецЕсли;
    КонецПроцедуры
    

    Важный нюанс: По умолчанию, после вызова ПолучитьИзВременногоХранилища() данные из хранилища удаляются. Если вам требуется сохранить данные во временном хранилище для повторного использования (например, при нескольких серверных вызовах), вы можете передать Ложь вторым параметром функции ПолучитьИзВременноеХранилище(): МоиДанные = ПолучитьИзВременногоХранилища(Адрес, Ложь);

Обратная передача измененных данных с сервера на клиент

Если после обработки на сервере вам необходимо вернуть какие-либо данные на клиент (например, измененную ТаблицуЗначений или результат обработки), процесс будет аналогичным. Мы снова используем временное хранилище.

  1. Подготовка и обработка данных на сервере.

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

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

    На клиенте мы вызываем эту серверную функцию и получаем адрес результата. Затем по этому адресу получаем измененную ТаблицуЗначений.

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

Работа с несколькими объектами: группировка данных

Часто возникает ситуация, когда нам нужно передать не один большой объект, а несколько различных объектов (например, ТаблицуЗначений, СписокЗначений и пару переменных) между клиентом и сервером. Вместо того чтобы помещать каждый объект в хранилище по отдельности и передавать несколько адресов, мы можем сгруппировать все эти объекты в один контейнер, такой как Структура или Массив, и уже этот контейнер поместить во временное хранилище.

Этот подход делает код более читаемым и упрощает управление данными.

Пример группировки на клиенте:


// На клиенте
МояТаблица1 = Новый ТаблицаЗначений;
// ... заполнение МояТаблица1

МойСписокЗначений = Новый СписокЗначений;
МойСписокЗначений.Добавить("Значение1");
МойСписокЗначений.Добавить("Значение2");

ВажнаяСтрока = "Дополнительная информация";

// Создаем структуру для группировки всех объектов
КонтейнерДанных = Новый Структура;
КонтейнерДанных.Вставить("ТаблицаОдин", МояТаблица1);
КонтейнерДанных.Вставить("СписокДанных", МойСписокЗначений);
КонтейнерДанных.Вставить("ДопСтрока", ВажнаяСтрока);

// Помещаем контейнер во временное хранилище
АдресКонтейнера = ПоместитьВоВременноеХранилище(КонтейнерДанных);

// Передаем адрес контейнера на сервер
ОбработатьКонтейнерНаСервере(АдресКонтейнера);

Пример получения группированных данных на сервере:


&НаСервере
Процедура ОбработатьКонтейнерНаСервере(АдресКонтейнера)

    // Получаем контейнер данных из временного хранилища
    ПолученныйКонтейнер = ПолучитьИзВременногоХранилища(АдресКонтейнера);

    Если ПолученныйКонтейнер <> Неопределено Тогда
        // Извлекаем объекты из структуры
        ТаблицаОдин = ПолученныйКонтейнер.ТаблицаОдин;
        СписокДанных = ПолученныйКонтейнер.СписокДанных;
        ДопСтрока = ПолученныйКонтейнер.ДопСтрока;

        Сообщить("На сервере получена таблица из " + ТаблицаОдин.Количество() + " строк.");
        Сообщить("Получена строка: " + ДопСтрока);
        // ... дальнейшая работа с извлеченными объектами
    Иначе
        Сообщить("Не удалось получить контейнер данных по адресу: " + АдресКонтейнера);
    КонецЕсли;
КонецПроцедуры

Важные нюансы и дополнительные возможности

При работе с временным хранилищем существуют несколько важных моментов, которые стоит учитывать для эффективной и надежной работы.

  1. Параметр ОбщийДоступ в ПоместитьВоВременноеХранилище().

    Как мы уже упоминали, функция ПоместитьВоВременноеХранилище() имеет необязательный второй параметр, который называется ОбщийДоступ. По умолчанию он равен Ложь. Если установить его в Истина, это означает, что помещенные данные будут доступны не только текущему пользователю, но и всем другим пользователям и сеансам, а также фоновым заданиям. Это крайне полезно, когда:

    • Вам нужно передать данные в фоновое задание.
    • Данные должны быть доступны для обработки несколькими независимыми серверными вызовами или из разных сессий.
    
    // Данные доступны всем пользователям и фоновым заданиям
    АдресОбщихДанных = ПоместитьВоВременноеХранилище(ОбъектДляОбщегоДоступа, Истина);
    

    Для управления такими процессами можно использовать фоновые задания с отслеживанием результата и индикацией прогресса без использования БСП.

  2. Время жизни адреса временного хранилища.

    Объекты во временном хранилище не хранятся вечно. Платформа 1С автоматически управляет их временем жизни. Обычно данные удаляются после получения их функцией ПолучитьИзВременногоХранилища() (если не указан второй параметр Ложь) или по истечении определенного таймаута без обращения к ним. Это предотвращает засорение памяти сервера неиспользуемыми данными. Поэтому не следует рассчитывать на долгосрочное хранение данных во временном хранилище. Если вам необходимо длительное хранение, используйте другие механизмы, такие как временные таблицы базы данных или хранение файлов.

  3. Принудительное удаление данных.

    Если вы уверены, что данные во временном хранилище больше не нужны и вы не хотите ждать автоматической очистки, вы можете принудительно удалить их с помощью функции УдалитьИзВременногоХранилища(), передав ей адрес данных.

    
    // Удаление данных из временного хранилища
    УдалитьИзВременногоХранилища(АдресБольшеНеНужныхДанных);
    

    Это может быть полезно для оптимизации потребления ресурсов сервера, особенно при работе с очень большими объемами данных.

  4. Типы данных, которые можно помещать.

    Во временное хранилище можно помещать любые объекты, которые поддерживают сериализацию. Это практически все стандартные объекты 1С: ТаблицыЗначений, СпискиЗначений, Массивы, Структуры, Соответствия, ДвоичныеДанные, примитивные типы (строки, числа, даты) и даже ссылки на объекты базы данных. Однако объекты, содержащие ссылки на формы, элементы форм или другие клиентские объекты, не могут быть сериализованы и, соответственно, помещены во временное хранилище.

  5. Проверка существования данных.

    Иногда полезно проверить, существуют ли данные по определенному адресу во временном хранилище, прежде чем пытаться их получить. Для этого служит функция СуществуетВременноеХранилище().

    
    // Проверяем наличие данных по адресу
    Если СуществуетВременноеХранилище(АдресДляПроверки) Тогда
        // Данные существуют, можно получать
        МоиДанные = ПолучитьИзВременногоХранилища(АдресДляПроверки);
    Иначе
        Сообщить("Данных по адресу " + АдресДляПроверки + " не найдено.");
    КонецЕсли;
    

    Эта функция возвращает Истина, если данные существуют, и Ложь в противном случае, помогая избежать ошибок при попытке получить уже удаленные или несуществующие данные.

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

Мы настоятельно рекомендуем тщательно проанализировать ваш сценарий использования и выбрать наиболее подходящие параметры для функций ПоместитьВоВременноеХранилище() и ПолучитьИзВременногоХранилища(), особенно в части управления временем жизни данных и их доступности.

← На главную