При работе с файловой системой в 1С часто возникает задача получить размер не отдельного файла, а целой папки со всем ее содержимым, включая вложенные подпапки и файлы. Эта задача имеет свои особенности, связанные как с программной реализацией, так и с правами доступа в операционной системе. Давайте подробно разберем, как мы можем решить эту проблему, рассмотрим различные подходы и выясним причины возможных ошибок.
Сперва мы могли бы подумать о простом решении, используя объект Файл и его свойство Размер, как было предложено на форуме. Например:
ФайлОбъект = Новый Файл("C:\МояПапка");
РазмерВБайтах = ФайлОбъект.Размер;
Однако, этот метод, к сожалению, работает только для отдельно взятых файлов. Если мы передадим ему путь к папке, свойство Размер вернет либо 0, либо вызовет ошибку, поскольку объект Файл предназначен для работы именно с файлами, а не с каталогами. Нам требуется более комплексное решение, которое позволит учитывать все вложенные элементы.
Рассмотрим два основных подхода к получению размера папки: рекурсивный обход средствами 1С и использование COM-объекта Scripting.FileSystemObject.
Этот метод является наиболее универсальным, поскольку он основан на встроенных возможностях платформы 1С и не зависит от операционной системы (хотя работа с файлами все равно специфична для ОС, сам подход рекурсии одинаков) или установленных COM-объектов. Суть его заключается в том, что мы создаем функцию, которая будет последовательно проходить по всем файлам и подпапкам внутри указанного каталога, суммируя размеры файлов.
НайтиФайлы() объекта Каталог для получения массива всех элементов (файлов и подпапок) в текущем каталоге.ЭтоКаталог() вернет Ложь), мы прибавляем его размер (ВыбранныйФайл.Размер()) к общей сумме.ЭтоКаталог() вернет Истина), мы рекурсивно вызываем нашу же функцию, передавая ей путь к этой подпапке.Представим себе функцию, которая будет подсчитывать размер. Мы будем использовать вспомогательную переменную для накопления общего размера.
&НаСервере
Функция ПолучитьРазмерКаталогаРекурсивно(ПутьККаталогу)
ОбщийРазмерВБайтах = 0;
КаталогФС = Новый Каталог(ПутьККаталогу);
Если Не КаталогФС.Существует() Тогда
Возврат 0;
КонецЕсли;
// Получаем все файлы и подпапки в текущем каталоге
Для Каждого Элемент Из КаталогФС.НайтиФайлы() Цикл
Если Элемент.ЭтоКаталог() Тогда
// Если это каталог, рекурсивно вызываем функцию для него
Попытка
ОбщийРазмерВБайтах = ОбщийРазмерВБайтах + ПолучитьРазмерКаталогаРекурсивно(Элемент.ПолноеИмя);
Исключение
// Обработка ошибок доступа к подкаталогу, например, можно пропустить
Сообщить("Не удалось получить доступ к подкаталогу: " + Элемент.ПолноеИмя + ". " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
Иначе
// Если это файл, добавляем его размер
Попытка
ОбщийРазмерВБайтах = ОбщийРазмерВБайтах + Элемент.Размер();
Исключение
// Обработка ошибок доступа к файлу
Сообщить("Не удалось получить доступ к файлу: " + Элемент.ПолноеИмя + ". " + ОписаниеОшибки(), СтатусСообщения.Важное);
КонецПопытки;
КонецЕсли;
КонецЦикла;
Возврат ОбщийРазмерВБайтах;
КонецФункции
// Пример использования:
// ПутьКОсновнойПапке = "C:\МоиДокументы\МояБольшаяПапка";
// РазмерИтого = ПолучитьРазмерКаталогаРекурсивно(ПутьКОсновнойПапке);
// Сообщить("Общий размер папки: " + РазмерИтого + " байт");
Этот метод является надежным, но для очень больших и глубоко вложенных структур папок он может быть медленным из-за многочисленных обращений к файловой системе и рекурсивных вызовов. Мы можем обернуть операции с файлами в блоки Попытка...Исключение, чтобы избежать полного падения при обнаружении недоступных элементов и хотя бы получить размер доступной части.
Для операционных систем семейства Windows мы можем использовать COM-объект Scripting.FileSystemObject, который предоставляет более простой способ получения размера папки, так как он имеет встроенный метод для этого. Однако этот метод имеет свои нюансы, связанные с правами доступа.
Scripting.FileSystemObject:Сначала мы создаем объект Scripting.FileSystemObject, затем используем его метод GetFolder(), чтобы получить объект, представляющий папку, а затем обращаемся к его свойству Size.
&НаСервере
Функция ПолучитьРазмерКаталогаЧерезFSO(ПутьККаталогу)
Попытка
FSO = Новый COMОбъект("Scripting.FileSystemObject");
File = FSO.GetFolder(ПутьККаталогу);
Возврат File.Size;
Исключение
Сообщить("Ошибка при получении размера папки через FSO: " + ОписаниеОшибки(), СтатусСообщения.Важное);
Возврат 0; // Или можно бросить исключение дальше
КонецПопытки;
КонецФункции
// Пример использования:
// ПутьКОсновнойПапке = "C:\МоиДокументы\МояБольшаяПапка";
// РазмерИтого = ПолучитьРазмерКаталогаЧерезFSO(ПутьКОсновнойПапке);
// Сообщить("Общий размер папки: " + РазмерИтого + " байт");
На форуме мы увидели, что при попытке использовать Scripting.FileSystemObject, возникла ошибка 0x800a0046. Это очень распространенная проблема, и она сигнализирует о недостаточных правах доступа.
Давайте выясним причину и разберем ее подробнее:
Scripting.FileSystemObject пытается пройти по всем элементам и, наткнувшись на недоступный, выдает ошибку.Для успешного использования Scripting.FileSystemObject или любого другого метода доступа к файловой системе из 1С, нам необходимо обеспечить соответствующие права доступа для пользователя, от имени которого работает процесс 1С. Вот несколько подходов:
\\Server\Exchange\МояПапка).Попытка...Исключение. Как было упомянуто на форуме, вы можете обернуть код получения размера в блок Попытка...Исключение. Это не решит проблему прав полностью, но позволит вашему коду не останавливаться, если некоторые файлы или подпапки недоступны, и получить размер хотя бы доступной части.&НаСервере. Если вам нужно передать файл с клиента на сервер или наоборот, используйте временное хранилище 1С.
&НаСервере
Функция ОтформатироватьРазмерФайла(РазмерВБайтах)
Если РазмерВБайтах < 1024 Тогда
Возврат Строка(РазмерВБайтах) + " байт";
ИначеЕсли РазмерВБайтах < 1024 * 1024 Тогда
Возврат СтрЗаменить(Формат(РазмерВБайтах / 1024, "ЧДЦ=2"), ",", ".") + " КБ";
ИначеЕсли РазмерВБайтах < 1024 * 1024 * 1024 Тогда
Возврат СтрЗаменить(Формат(РазмерВБайтах / (1024 * 1024), "ЧДЦ=2"), ",", ".") + " МБ";
Иначе
Возврат СтрЗаменить(Формат(РазмерВБайтах / (1024 * 1024 * 1024), "ЧДЦ=2"), ",", ".") + " ГБ";
КонецЕсли;
КонецФункции
// Пример использования:
// РазмерИтого = ПолучитьРазмерКаталогаРекурсивно(ПутьКОсновнойПапке);
// Сообщить("Общий размер папки: " + ОтформатироватьРазмерФайла(РазмерИтого));
В заключение, мы выяснили, что для получения размера папки в 1С у нас есть два основных пути: универсальный рекурсивный обход, который всегда будет работать при наличии прав, и более простой в написании, но зависящий от ОС и прав доступа Scripting.FileSystemObject. В каждом случае критически важно обеспечить корректные права доступа для пользователя, от имени которого выполняется код 1С. Выбирайте метод, который лучше всего соответствует вашим требованиям к универсальности и производительности, не забывая о безопасности и управлении правами доступа.