Как выгрузить файлы из ХранилищаЗначения в 1С и сохранить их в локальную или сетевую папку?

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

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

Извлечение данных из хранилища

Для начала рассмотрим, как вообще получить данные из реквизита типа ХранилищеЗначения. В большинстве случаев внутри хранятся ДвоичныеДанные. Чтобы извлечь их, мы используем метод Получить().

Посмотрим на пример базового извлечения с проверкой типа (как советуют опытные разработчики для избежания ошибок при выполнении кода):


ФайловоеХранилище = Объект.Хранилище;
Если ТипЗнч(ФайловоеХранилище) = Тип("ХранилищеЗначения") Тогда
    ИзвлеченныеДанные = ФайловоеХранилище.Получить();
    Если ТипЗнч(ИзвлеченныеДанные) = Тип("ДвоичныеДанные") Тогда
        // Здесь будет код сохранения в файл
    КонецЕсли;
КонецЕсли;

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

Сохранение файла на сервере и вопросы прав доступа

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


ИмяФайла = "\\server\shared_folder\document_123.pdf";
ИзвлеченныеДанные.Записать(ИмяФайла);

Проанализируем критически важный нюанс: права доступа. При выполнении этого кода в серверном контексте 1С, запись файла инициируется не от имени текущего пользователя Windows, а от имени системного пользователя, под которым работает служба сервера 1С (чаще всего это локальный пользователь USR1CV8). У этой учетной записи по умолчанию нет прав на запись в сетевые папки. Поэтому перед запуском алгоритма обязательно обратитесь к системному администратору, чтобы он предоставил пользователю USR1CV8 права на изменение целевой сетевой папки.

Проблема потери имени и расширения файла

Выясним еще одну неочевидную причину возможных ошибок. Сами по себе ДвоичныеДанные — это просто массив байтов. Они не знают, как назывался исходный файл и какое у него было расширение. Если при начальной загрузке в базу в ХранилищеЗначения помещались исключительно двоичные данные, оригинальные имена утеряны.

Что делать в такой ситуации?

  1. Генерировать имена самостоятельно на основе реквизитов документа (например, использовать номер и дату: "Счет_" + Объект.Номер + ".pdf").
  2. Если предыдущий программист все сделал правильно, в хранилище лежит не просто файл, а объект Структура, содержащий ключи ИмяФайла и Данные. В таком случае код извлечения будет выглядеть иначе.

Рассмотрим пример извлечения из структуры:


ДанныеХранилища = Объект.Хранилище.Получить();
Если ТипЗнч(ДанныеХранилища) = Тип("Структура") Тогда
    ДвоичныеДанные = ДанныеХранилища.Данные;
    ИмяФайла = ДанныеХранилища.ИмяФайла;
    ПолныйПуть = "\\server\shared_folder\" + ИмяФайла;
    ДвоичныеДанные.Записать(ПолныйПуть);
КонецЕсли;

Передача файла на клиент (Асинхронный подход)

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

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

Разберем этот механизм по шагам:

  1. На сервере получаем данные и помещаем их во временное хранилище с помощью метода ПоместитьВоВременноеХранилище(). Получаем строковый адрес.
  2. Возвращаем этот адрес на клиентскую сторону.
  3. На клиенте вызываем асинхронный метод НачатьПолучениеФайлаССервера(), передавая ему полученный адрес и желаемый путь сохранения.

Замена файла на ссылку и очистка хранилища

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

Рассмотрим, как это реализовать:

  1. В метаданных (в конфигураторе) добавьте в документ новый строковый реквизит, например ПутьКФайлу.
  2. После успешного сохранения файла на диск с помощью метода Записать(), присвойте этому реквизиту сформированный путь к файлу.
  3. Очистите ХранилищеЗначения. Для этого можно присвоить реквизиту Неопределено или создать пустое хранилище: Объект.Хранилище = Новый ХранилищеЗначения(Неопределено);.
  4. Обязательно вызовите метод Объект.Записать(), чтобы зафиксировать изменения и реально освободить место в таблицах базы данных.

Массовый перенос (Пакетная обработка)

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

Алгоритм массовой обработки должен быть следующим:

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

Важно: Обработку больших объемов данных лучше выполнять порциями (например, по 100-500 документов) и использовать транзакции. Конструкции НачатьТранзакцию() и ЗафиксироватьТранзакцию() помогут избежать ситуаций, когда из-за сбоя сети файл уже удален из базы, но еще не успел сохраниться на жесткий диск.

← На главную