В процессе разработки на платформе 1С часто возникает задача копирования не просто отдельного файла, а целого каталога со всей его иерархией. Стандартного метода вроде КопироватьКаталог() в языке 1С не существует, поэтому разработчики используют различные обходные пути: от рекурсивного обхода папок до вызова системных утилит Windows или Linux. В этой статье мы подробно разберем все эффективные методы реализации этой задачи, проанализируем их плюсы и минусы, а также предоставим готовые листинги кода.
Самый надежный и независимый от операционной системы способ — это использование встроенных функций 1С: НайтиФайлы, СоздатьКаталог и КопироватьФайл. Суть метода заключается в том, чтобы получить список всех файлов в исходной папке (включая подкаталоги), а затем для каждого элемента воссоздать путь в целевой папке.
Рассмотрим доработанный и проверенный код, который корректно обрабатывает структуру папок и копирует файлы:
Процедура КопироватьПереместитьКаталог(СтарыйПуть, НовыйПуть, Переместить = Ложь) Экспорт
// Проверим наличие завершающего слэша в пути назначения
Если Прав(НовыйПуть, 1) <> "\" И Прав(НовыйПуть, 1) <> "/" Тогда
НовыйПуть = НовыйПуть + "/";
КонецЕсли;
// Получаем имя исходного каталога, чтобы создать его внутри целевой папки
ФайлИсточник = Новый Файл(СтарыйПуть);
ИмяПапкиИсточника = ФайлИсточник.Имя;
ПутьНазначенияСПапкой = НовыйПуть + ИмяПапкиИсточника;
// Создаем корневой каталог в новом месте
СоздатьКаталог(ПутьНазначенияСПапкой);
// Ищем все файлы и подкаталоги рекурсивно (параметр Истина)
НайденныеОбъекты = НайтиФайлы(СтарыйПуть, "*.*", Истина);
Для Каждого Объект Из НайденныеОбъекты Цикл
// Формируем новый путь путем замены старого префикса на новый
НовыйПолныйПуть = СтрЗаменить(Объект.ПолноеИмя, СтарыйПуть, ПутьНазначенияСПапкой);
Если Объект.ЭтоКаталог Тогда
// Если это папка — просто создаем её
СоздатьКаталог(НовыйПолныйПуть);
Иначе
// Перед копированием файла убедимся, что целевой каталог для него существует
// Это важно, если дерево каталогов сложное
ПапкаФайла = Новый Файл(НовыйПолныйПуть);
Если НЕ Файл(ПапкаФайла.Путь).Существует() Тогда
СоздатьКаталог(ПапкаФайла.Путь);
КонецЕсли;
Если Переместить Тогда
ПереместитьФайл(Объект.ПолноеИмя, НовыйПолныйПуть);
Иначе
КопироватьФайл(Объект.ПолноеИмя, НовыйПолныйПуть);
КонецЕсли;
КонецЕсли;
КонецЦикла;
// Если была команда перемещения, удаляем исходный каталог в конце
Если Переместить Тогда
УдалитьФайлы(СтарыйПуть);
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию: данный код использует параметр Истина в методе НайтиФайлы, что позволяет платформе самостоятельно выполнить глубокий поиск. Важный момент здесь — функция СтрЗаменить, которая "перебрасывает" файл из старой иерархии в новую. Этот метод работает одинаково хорошо как под Windows, так и под Linux.
Если решение разрабатывается исключительно под Windows и вам нужно написать минимум строк кода, можно воспользоваться объектом Scripting.FileSystemObject. Выясним причину его популярности: он позволяет копировать папки целиком одной командой.
Процедура СкопироватьПапкуЧерезFSO(ПутьИсточник, ПутьНазначение)
Попытка
FSO = Новый COMОбъект("Scripting.FileSystemObject");
// Метод CopyFolder(Источник, Назначение, Перезаписывать)
// Параметр Перезаписывать = Истина (True)
FSO.CopyFolder(ПутьИсточник, ПутьНазначение, Истина);
Исключение
Сообщить("Ошибка при копировании: " + ОписаниеОшибки());
КонецПопытки;
КонецПроцедуры
Важные нюансы: метод CopyFolder автоматически создает структуру папок. Однако стоит помнить, что этот метод не работает в веб-клиенте и на серверах под управлением Linux. Кроме того, при копировании заблокированных файлов может возникнуть ошибка, которую нужно обрабатывать блоком Попытка...Исключение.
Для копирования очень больших объемов данных (терабайты файлов) или при необходимости зеркалирования папок лучше всего использовать специализированные системные утилиты. Рассмотрим пример использования robocopy, которая считается стандартом де-факто для Windows.
Разберем по шагам выполнение команды через 1С:
ЗапуститьПриложение./S или /E).
// Пример для Windows (Robocopy)
Команда = "robocopy """ + ИсходныйПуть + """ """ + ЦелевойПуть + """ /E /COPYALL /R:3 /W:5";
ЗапуститьПриложение(Команда,, Истина); // Ждем завершения (Истина)
// Пример для Linux
Команда = "cp -r " + ИсходныйПуть + " " + ЦелевойПуть;
ЗапуститьПриложение(Команда,, Истина);
Обратите внимание: использование кавычек в строке команды обязательно, если в путях присутствуют пробелы или русские символы. Утилита robocopy также позволяет возобновлять копирование после сбоев, что крайне полезно для регламентных заданий.
Интересный и очень лаконичный способ скопировать всю папку со всеми вложенными элементами — использовать встроенные средства работы с архивами 1С. Платформа при упаковке и распаковке идеально сохраняет всю структуру подкаталогов.
Процедура КопироватьКаталогЧерезZip(Источник, Назначение)
ВременныйФайл = ПолучитьИмяВременногоФайла("zip");
// Запаковываем
Архив = Новый ЗаписьZipФайла(ВременныйФайл);
Архив.Добавить(Источник, РежимСохраненияПутейZIP.СохранятьОтносительныеПути);
Архив.Записать();
// Распаковываем в новое место
Чтение = Новый ЧтениеZipФайла(ВременныйФайл);
Чтение.ИзвлечьВсе(Назначение, РежимВосстановленияПутейФайловZIP.Восстанавливать);
Чтение.Закрыть();
УдалитьФайлы(ВременныйФайл);
КонецПроцедуры
Этот метод избавляет разработчика от написания рекурсии и ручного создания каждой вложенной папки. Платформа 1С берет всю рутину на себя.
При реализации копирования в реальных задачах, мы рекомендуем придерживаться следующих правил:
Попытка...Исключение. Если один файл из тысячи занят другим процессом, стандартный метод 1С прервет выполнение всей обработки.НачатьКопированиеФайла, чтобы интерфейс пользователя не "зависал" на время операции.USR1CV8). У этой учетной записи должны быть права на чтение источника и запись в каталог назначения (особенно если это сетевой путь UNC).КаталогВременныхФайлов(), это гарантирует, что у системы будут права на запись в эту область.Выяснив все доступные методы, мы можем сделать вывод: для простых задач внутри Windows оптимален FileSystemObject, для кроссплатформенности и полного контроля — рекурсивный обход через НайтиФайлы, а для надежного бэкапа больших данных — вызов robocopy.