Как получить размер папки в 1С, включая вложенные файлы и подпапки?

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

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

Сперва мы могли бы подумать о простом решении, используя объект Файл и его свойство Размер, как было предложено на форуме. Например:


ФайлОбъект = Новый Файл("C:\МояПапка");
РазмерВБайтах = ФайлОбъект.Размер;

Однако, этот метод, к сожалению, работает только для отдельно взятых файлов. Если мы передадим ему путь к папке, свойство Размер вернет либо 0, либо вызовет ошибку, поскольку объект Файл предназначен для работы именно с файлами, а не с каталогами. Нам требуется более комплексное решение, которое позволит учитывать все вложенные элементы.

Рассмотрим два основных подхода к получению размера папки: рекурсивный обход средствами 1С и использование COM-объекта Scripting.FileSystemObject.

Решение 1: Рекурсивный обход папок средствами 1С (универсальный метод)

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

Пошаговая реализация рекурсивного обхода:

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

Пример кода для рекурсивного обхода:

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


&НаСервере
Функция ПолучитьРазмерКаталогаРекурсивно(ПутьККаталогу)

    ОбщийРазмерВБайтах = 0;

    КаталогФС = Новый Каталог(ПутьККаталогу);
    Если Не КаталогФС.Существует() Тогда
        Возврат 0;
    КонецЕсли;

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

    Возврат ОбщийРазмерВБайтах;

КонецФункции

// Пример использования:
// ПутьКОсновнойПапке = "C:\МоиДокументы\МояБольшаяПапка";
// РазмерИтого = ПолучитьРазмерКаталогаРекурсивно(ПутьКОсновнойПапке);
// Сообщить("Общий размер папки: " + РазмерИтого + " байт");

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

Решение 2: Использование COMОбъект("Scripting.FileSystemObject") (метод для Windows)

Для операционных систем семейства Windows мы можем использовать COM-объект Scripting.FileSystemObject, который предоставляет более простой способ получения размера папки, так как он имеет встроенный метод для этого. Однако этот метод имеет свои нюансы, связанные с правами доступа.

Пример кода с Scripting.FileSystemObject:

Сначала мы создаем объект Scripting.FileSystemObject, затем используем его метод GetFolder(), чтобы получить объект, представляющий папку, а затем обращаемся к его свойству Size.


&НаСервере
Функция ПолучитьРазмерКаталогаЧерезFSO(ПутьККаталогу)
    Попытка
        FSO = Новый COMОбъект("Scripting.FileSystemObject");
        File = FSO.GetFolder(ПутьККаталогу);
        Возврат File.Size;
    Исключение
        Сообщить("Ошибка при получении размера папки через FSO: " + ОписаниеОшибки(), СтатусСообщения.Важное);
        Возврат 0; // Или можно бросить исключение дальше
    КонецПопытки;
КонецФункции

// Пример использования:
// ПутьКОсновнойПапке = "C:\МоиДокументы\МояБольшаяПапка";
// РазмерИтого = ПолучитьРазмерКаталогаЧерезFSO(ПутьКОсновнойПапке);
// Сообщить("Общий размер папки: " + РазмерИтого + " байт");

Проблемы с правами доступа и ошибка 0x800a0046

На форуме мы увидели, что при попытке использовать Scripting.FileSystemObject, возникла ошибка 0x800a0046. Это очень распространенная проблема, и она сигнализирует о недостаточных правах доступа.

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

  1. Различие в контексте выполнения.
    • Если код выполняется на клиенте 1С (толстый, тонкий или веб-клиент), то он работает от имени текущего пользователя операционной системы, который запустил клиентское приложение. Права доступа к файлам и папкам будут соответствовать правам этого пользователя.
    • Если код выполняется на сервере 1С, то он работает от имени пользователя ОС, под которым запущен сервер 1С:Предприятия. Это может быть системная учетная запись (например, "Local System" или "Network Service") или специально выделенный доменный/локальный пользователь. В случае файловой базы, опубликованной на веб-сервере, доступ будет ограничен правами пользователя, от имени которого запущен веб-сервер.
  2. Неполные права к содержимому. Вы могли заметить, что можете вручную открыть папку и посмотреть ее размер, но программный код выдает ошибку. Причина в том, что даже если у вас есть права на саму папку, внутри нее могут находиться файлы или подпапки, к которым у пользователя, от имени которого запущен процесс 1С, нет прав. Scripting.FileSystemObject пытается пройти по всем элементам и, наткнувшись на недоступный, выдает ошибку.

Как решить проблему с правами доступа?

Для успешного использования Scripting.FileSystemObject или любого другого метода доступа к файловой системе из 1С, нам необходимо обеспечить соответствующие права доступа для пользователя, от имени которого работает процесс 1С. Вот несколько подходов:

  1. Настройка сетевого доступа и разрешений.
    • Если доступ к файлам осуществляется по сети, обязательно используйте абсолютный UNC-путь (например, \\Server\Exchange\МояПапка).
    • Для пользователя Windows, от имени которого запущен сервер 1С:Предприятия, необходимо настроить полный доступ к этому сетевому ресурсу. Рекомендуем на начальном этапе для тестирования использовать группу пользователей "Все" с полными разрешениями на сетевую папку и затем сузить их до конкретного пользователя сервера 1С.
  2. Запуск агента сервера 1С от доменного пользователя. Если сервер 1С работает в домене и сетевые ресурсы также доменные, то наиболее надежным решением является запуск агента сервера 1С от имени доменного пользователя, который имеет необходимые права доступа ко всем требуемым сетевым папкам.
  3. Создание локального пользователя. В некоторых случаях, если сетевые папки находятся на разных серверах, может потребоваться создание на этих серверах локального пользователя с такими же учетными данными (имя пользователя и пароль), как у пользователя, от имени которого запущен сервер 1С. Это позволит обеспечить "сквозную" аутентификацию.
  4. Использование Попытка...Исключение. Как было упомянуто на форуме, вы можете обернуть код получения размера в блок Попытка...Исключение. Это не решит проблему прав полностью, но позволит вашему коду не останавливаться, если некоторые файлы или подпапки недоступны, и получить размер хотя бы доступной части.

Дополнительные рекомендации и лучшие практики

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

← На главную