Довольно часто перед разработчиками встает задача оптимизации размера базы данных 1С. Если изначально файлы загружались непосредственно в базу и хранились в реквизитах с типом ХранилищеЗначения, со временем база может разрастись до огромных размеров. Чтобы решить эту проблему, файлы необходимо перенести в отдельную сетевую или локальную папку, а в самой базе оставить лишь ссылки (пути) на эти файлы. Разберем по шагам, как правильно и безопасно выполнить этот перенос.
Для начала рассмотрим, как вообще получить данные из реквизита типа ХранилищеЗначения. В большинстве случаев внутри хранятся ДвоичныеДанные. Чтобы извлечь их, мы используем метод Получить().
Посмотрим на пример базового извлечения с проверкой типа (как советуют опытные разработчики для избежания ошибок при выполнении кода):
ФайловоеХранилище = Объект.Хранилище;
Если ТипЗнч(ФайловоеХранилище) = Тип("ХранилищеЗначения") Тогда
ИзвлеченныеДанные = ФайловоеХранилище.Получить();
Если ТипЗнч(ИзвлеченныеДанные) = Тип("ДвоичныеДанные") Тогда
// Здесь будет код сохранения в файл
КонецЕсли;
КонецЕсли;
Проверка типа данных, которые поместили в хранилище, крайне важна. Если попытаться применить методы двоичных данных к другому типу (например, если туда случайно записали строку или структуру), система выдаст ошибку. Учитесь писать код правильно и всегда предусматривайте возможные отклонения.
Если наша задача — выгрузить файлы в общую сетевую директорию, мы можем выполнить сохранение сразу на стороне сервера. Для этого у объекта ДвоичныеДанные есть простой метод Записать(), в который передается полный путь.
ИмяФайла = "\\server\shared_folder\document_123.pdf";
ИзвлеченныеДанные.Записать(ИмяФайла);
Проанализируем критически важный нюанс: права доступа. При выполнении этого кода в серверном контексте 1С, запись файла инициируется не от имени текущего пользователя Windows, а от имени системного пользователя, под которым работает служба сервера 1С (чаще всего это локальный пользователь USR1CV8). У этой учетной записи по умолчанию нет прав на запись в сетевые папки. Поэтому перед запуском алгоритма обязательно обратитесь к системному администратору, чтобы он предоставил пользователю USR1CV8 права на изменение целевой сетевой папки.
Выясним еще одну неочевидную причину возможных ошибок. Сами по себе ДвоичныеДанные — это просто массив байтов. Они не знают, как назывался исходный файл и какое у него было расширение. Если при начальной загрузке в базу в ХранилищеЗначения помещались исключительно двоичные данные, оригинальные имена утеряны.
Что делать в такой ситуации?
"Счет_" + Объект.Номер + ".pdf").Структура, содержащий ключи ИмяФайла и Данные. В таком случае код извлечения будет выглядеть иначе.Рассмотрим пример извлечения из структуры:
ДанныеХранилища = Объект.Хранилище.Получить();
Если ТипЗнч(ДанныеХранилища) = Тип("Структура") Тогда
ДвоичныеДанные = ДанныеХранилища.Данные;
ИмяФайла = ДанныеХранилища.ИмяФайла;
ПолныйПуть = "\\server\shared_folder\" + ИмяФайла;
ДвоичныеДанные.Записать(ПолныйПуть);
КонецЕсли;
Если стоит задача сохранить файл не в общую сетевую папку, а на локальный диск конкретного пользователя (на клиенте), мы не можем просто записать его с сервера. Двоичные данные необходимо передать на клиент.
Передавать объемные двоичные данные напрямую между клиентом и сервером — плохая практика, которая может привести к нехватке памяти. Современный подход требует использования временного хранилища и асинхронных методов.
Разберем этот механизм по шагам:
ПоместитьВоВременноеХранилище(). Получаем строковый адрес.НачатьПолучениеФайлаССервера(), передавая ему полученный адрес и желаемый путь сохранения.Поскольку главная цель — облегчить базу данных, нам нужно не только выгрузить файл, но и удалить его из базы, оставив ссылку.
Рассмотрим, как это реализовать:
ПутьКФайлу.Записать(), присвойте этому реквизиту сформированный путь к файлу.ХранилищеЗначения. Для этого можно присвоить реквизиту Неопределено или создать пустое хранилище: Объект.Хранилище = Новый ХранилищеЗначения(Неопределено);.Объект.Записать(), чтобы зафиксировать изменения и реально освободить место в таблицах базы данных.Так как перенести нужно все данные из базы, вручную открывать каждый документ бессмысленно. Необходимо написать внешнюю обработку.
Алгоритм массовой обработки должен быть следующим:
ХранилищеЗначения не является пустым.Выборка.ПолучитьОбъект()).Важно: Обработку больших объемов данных лучше выполнять порциями (например, по 100-500 документов) и использовать транзакции. Конструкции НачатьТранзакцию() и ЗафиксироватьТранзакцию() помогут избежать ситуаций, когда из-за сбоя сети файл уже удален из базы, но еще не успел сохраниться на жесткий диск.