Как программно скопировать файл или папку на встроенном языке 1С
Содержание
Чтобы скопировать файл в 1С программно, хватает одной строки: КопироватьФайл("C:\Обмен\in.xml", "D:\Архив\in.xml"). Метод глобального контекста, работает на клиенте и на сервере, файл-приёмник с тем же именем перезаписывается. Для больших файлов, передачи между клиентом и сервером и копирования целых папок нужны другие методы: двоичные данные, файловые потоки, COM-объект Scripting.FileSystemObject или системные утилиты.
В клиентском коде новых конфигураций вместо синхронного метода используют КопироватьФайлАсинх (платформа 8.3.18 и выше). Любой вызов оборачивайте в Попытка: занятый файл или отсутствие прав прерывают операцию исключением.
Где выполняется код и что проверить заранее
Главная ловушка при копировании файлов в 1С — не синтаксис, а место выполнения кода. Путь C:\Обмен в серверной процедуре указывает на диск сервера 1С, а не на компьютер пользователя.
🖥️ Файловая база, тонкий клиент. Клиентский и серверный код выполняются на одном компьютере. Пути одинаковые, права — права пользователя Windows.
🏢 Клиент-серверная база
. Код с директивой &НаСервере выполняется рабочим процессом кластера. Он работает под учётной записью службы сервера 1С (по умолчанию USR1CV8). Именно этой учётной записи нужны права на чтение источника и запись в каталог-приёмник, включая сетевые папки.
🌐 Веб-клиент. Прямого доступа к файловой системе нет. Нужно расширение работы с файлами и только асинхронные методы.
Перед запуском проверьте ещё три вещи:
- Безопасный режим. Внешние обработки и расширения в безопасном режиме не могут работать с файлами. Код выдаст исключение о запрете операции.
- Профили безопасности кластера. Если администратор их включил, доступ к каталогам разрешается явно.
- Сетевые пути. Указывайте UNC-путь
\\Сервер\Обмен\, а не подключённый дискZ:\. У службы сервера 1С подключённых дисков пользователя нет.
Быстрая проверка места выполнения: вызовите в процедуре КаталогВременныхФайлов() и выведите результат через Сообщить. Путь сразу покажет, на чьём компьютере работает код.
КопироватьФайл: базовый способ копирования файла в 1С
Синтаксис: КопироватьФайл(ИмяФайлаИсточника, ИмяФайлаПриемника). Оба параметра — полные пути с именем файла. Указать вторым параметром только каталог нельзя: имя приёмника нужно передать целиком.
&НаСервере
Процедура СкопироватьВыгрузку()
Источник = "\\FS01\Обмен\выгрузка.xml";
КаталогАрхива = "\\FS01\Архив\" + Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd") + "\";
ФайлИсточника = Новый Файл(Источник);
Если Не ФайлИсточника.Существует() Тогда
ВызватьИсключение "Файл не найден: " + Источник;
КонецЕсли;
// Каталог-приёмник КопироватьФайл сам не создаёт
СоздатьКаталог(КаталогАрхива);
КопироватьФайл(Источник, КаталогАрхива + ФайлИсточника.Имя);
КонецПроцедуры
Что важно знать про метод:
- 📁 Каталог-приёмник должен существовать.
СоздатьКаталогсоздаёт и вложенные папки, а на существующем каталоге ошибку не выдаёт. - ♻️ Перезапись без вопросов. Файл с тем же именем заменяется. Если старую версию нужно сохранить, проверьте
Новый Файл(Приемник).Существует()и добавьте к имени суффикс. - 🔒 Только чтение. Если у файла-приёмника стоит атрибут «только чтение», копирование упадёт с исключением. Снимите атрибут:
ФайлПриемника.УстановитьТолькоЧтение(Ложь). - 🔁 Перемещение. Для переноса без копии есть
ПереместитьФайл(Источник, Приемник), для удаления —УдалитьФайлы(Путь).
Как скопировать папку целиком
Отдельного метода копирования каталога во встроенном языке нет. Обходим дерево через НайтиФайлы с флагом рекурсии и воссоздаём структуру:
&НаСервере
Процедура СкопироватьКаталог(Знач Источник, Знач Приемник)
Разделитель = ПолучитьРазделительПути();
Если Не СтрЗаканчиваетсяНа(Источник, Разделитель) Тогда
Источник = Источник + Разделитель;
КонецЕсли;
Если Не СтрЗаканчиваетсяНа(Приемник, Разделитель) Тогда
Приемник = Приемник + Разделитель;
КонецЕсли;
СоздатьКаталог(Приемник);
ДлинаИсточника = СтрДлина(Источник);
Для Каждого Элемент Из НайтиФайлы(Источник, "*", Истина) Цикл
ОтносительныйПуть = Сред(Элемент.ПолноеИмя, ДлинаИсточника + 1);
Если Элемент.ЭтоКаталог() Тогда
СоздатьКаталог(Приемник + ОтносительныйПуть);
Иначе
СоздатьКаталог(Приемник + Сред(Элемент.Путь, ДлинаИсточника + 1));
КопироватьФайл(Элемент.ПолноеИмя, Приемник + ОтносительныйПуть);
КонецЕсли;
КонецЦикла;
КонецПроцедуры
ПолучитьРазделительПути() делает код переносимым: на сервере под Linux разделитель — /. Свойство Путь у объекта Файл возвращает каталог файла с завершающим разделителем, поэтому повторный вызов СоздатьКаталог для уже созданной папки безопасен.
Копирование через двоичные данные и буфер в памяти
Объект ДвоичныеДанные читает файл в память целиком. Это удобно, когда файл нужно не просто скопировать, а передать: с сервера на клиент, в реквизит типа ХранилищеЗначения, во вложение письма или в HTTP-запрос.
Данные = Новый ДвоичныеДанные("C:\Шаблоны\договор.docx");
Данные.Записать("C:\Документы\договор_125.docx");
Две строки дают ту же копию, что и КопироватьФайл, но с возможностью вмешаться между чтением и записью: посчитать хеш, проверить размер Данные.Размер(), сохранить в базу.
Передача файла между сервером и клиентом
Классический сценарий: на сервере сформировали файл, пользователю нужно сохранить его у себя. Прямого «копирования с сервера на клиент» нет, работает связка с временным хранилищем:
&НаСервере
Функция АдресФайлаОтчета()
Данные = Новый ДвоичныеДанные("\\FS01\Отчеты\сверка.xlsx");
Возврат ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
КонецФункции
&НаКлиенте
Процедура СохранитьОтчет(Команда)
Адрес = АдресФайлаОтчета();
Данные = ПолучитьИзВременногоХранилища(Адрес);
Данные.Записать("C:\Отчеты\сверка.xlsx");
КонецПроцедуры
Второй параметр ПоместитьВоВременноеХранилище — идентификатор формы. Данные живут, пока открыта форма, и не копятся в памяти сервера.
Буфер двоичных данных
БуферДвоичныхДанных — область памяти фиксированного размера, которую можно читать и менять побайтно. Сам по себе для копирования файла он не нужен, зато незаменим в связке с потоками: через него файл переносится порциями, а не целиком.
ДвоичныеДанные держат весь файл в оперативной памяти. Копия файла на 2–3 ГБ таким способом на сервере может съесть память рабочего процесса и замедлить работу всех пользователей. Для крупных файлов берите КопироватьФайл или потоки.
Потоковое копирование больших файлов
Файловые потоки появились в платформе 8.3.9. Они читают и пишут файл частями, поэтому расход памяти не зависит от размера файла. Самый короткий вариант — метод КопироватьВ:
&НаСервере
Процедура СкопироватьПотоком(Источник, Приемник)
ПотокЧтения = ФайловыеПотоки.ОткрытьДляЧтения(Источник);
ПотокЗаписи = ФайловыеПотоки.Создать(Приемник);
Попытка
ПотокЧтения.КопироватьВ(ПотокЗаписи, 1048576); // буфер 1 МБ
Исключение
ПотокЧтения.Закрыть();
ПотокЗаписи.Закрыть();
ВызватьИсключение;
КонецПопытки;
ПотокЧтения.Закрыть();
ПотокЗаписи.Закрыть();
КонецПроцедуры
Если нужен контроль над каждой порцией — показать прогресс, остановиться по флагу, посчитать контрольную сумму на лету, — читайте вручную через буфер:
РазмерБуфера = 1048576;
Буфер = Новый БуферДвоичныхДанных(РазмерБуфера);
Всего = ПотокЧтения.Размер();
Скопировано = 0;
Прочитано = ПотокЧтения.Прочитать(Буфер, 0, РазмерБуфера);
Пока Прочитано > 0 Цикл
ПотокЗаписи.Записать(Буфер, 0, Прочитано);
Скопировано = Скопировано + Прочитано;
// здесь можно обновить прогресс: Скопировано / Всего * 100
Прочитано = ПотокЧтения.Прочитать(Буфер, 0, РазмерБуфера);
КонецЦикла;
Записывайте ровно Прочитано байт, а не весь буфер. Последняя порция почти всегда короче, и запись полного буфера добавит в конец копии мусор из предыдущего чтения.
Буфер от 64 КБ до нескольких мегабайт — разумный диапазон. Меньше — много обращений к диску, больше — выигрыша по скорости обычно нет.
COM-объект и системные утилиты
На Windows можно поручить копирование самой операционной системе. Это полезно, когда нужно скопировать папку с сотнями файлов или продолжать с места обрыва по сети.
Scripting.FileSystemObject
FSO = Новый COMОбъект("Scripting.FileSystemObject");
FSO.CopyFile("C:\Обмен\*.xml", "D:\Архив\", Истина);
FSO.CopyFolder("C:\Обмен\Фото", "D:\Архив\Фото", Истина);
Третий параметр Истина разрешает перезапись. В отличие от КопироватьФайл, CopyFile принимает маску и каталог-приёмник с завершающим \.
robocopy через КомандаСистемы или ЗапуститьПриложение
Команда = "robocopy ""\\FS01\Обмен"" ""D:\Архив\Обмен"" /E /R:3 /W:5";
ЗапуститьПриложение(Команда, , Истина);
Ключи /E — с подкаталогами, включая пустые, /R:3 — три повтора при ошибке, /W:5 — пауза 5 секунд между повторами. Третий параметр ЗапуститьПриложение со значением Истина заставляет 1С дождаться окончания команды.
| Способ | Плюсы | Ограничения |
|---|---|---|
Scripting.FileSystemObject | Маски, копирование папок одной строкой | Только Windows, нет в веб-клиенте |
robocopy | Повторы, докачка, журнал копирования | Только Windows, код возврата нужно разбирать отдельно |
cp -r на Linux-сервере | Встроен в систему | Права пользователя службы, другой синтаксис путей |
COM и запуск внешних программ на сервере 1С выполняются от имени службы. В безопасном режиме и при включённых профилях безопасности они запрещены. Для типовых конфигураций на поддержке встроенные методы надёжнее.
Обработка ошибок при копировании
Копирование падает по понятным причинам: файла нет, нет прав, файл открыт другой программой, закончилось место. Без перехвата пользователь увидит системное окно и прерванную операцию. Оборачивайте вызов в Попытка и пишите подробности в журнал регистрации:
&НаСервере
Функция СкопироватьБезопасно(Источник, Приемник)
Попытка
КопироватьФайл(Источник, Приемник);
Исключение
Подробно = ПодробноеПредставлениеОшибки(ИнформацияОбОшибке());
ЗаписьЖурналаРегистрации("Копирование файлов",
УровеньЖурналаРегистрации.Ошибка, , ,
СтрШаблон("%1 -> %2
|%3", Источник, Приемник, Подробно));
Возврат Ложь;
КонецПопытки;
// Проверка результата: файл есть и размер совпадает
ФайлИсточника = Новый Файл(Источник);
ФайлПриемника = Новый Файл(Приемник);
Возврат ФайлПриемника.Существует()
И ФайлПриемника.Размер() = ФайлИсточника.Размер();
КонецФункции
Пользователю показывайте КраткоеПредставлениеОшибки, в журнал пишите подробное. Для критичных файлов — выгрузок в банк, резервных копий — сравнивайте не только размер, но и хеш:
Хеш = Новый ХешированиеДанных(ХешФункция.MD5);
Хеш.ДобавитьФайл(Приемник);
КонтрольнаяСумма = Хеш.ХешСумма;
Асинхронное копирование и фоновые задания
В клиентском коде современных конфигураций синхронные вызовы работы с файлами не используют: веб-клиент их не поддерживает, а свойство конфигурации, отвечающее за синхронные вызовы расширений, в новых решениях обычно запрещает их.
КопироватьФайлАсинх (платформа 8.3.18 и выше)
&НаКлиенте
Асинх Процедура СкопироватьНаКлиенте(Команда)
Попытка
Ждать КопироватьФайлАсинх("C:\Обмен\in.xml", "C:\Архив\in.xml");
ПоказатьПредупреждение(, "Файл скопирован");
Исключение
ПоказатьПредупреждение(, КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
КонецПопытки;
КонецПроцедуры
Код читается как синхронный, но не блокирует интерфейс. Ошибку ловит обычная Попытка вокруг Ждать.
НачатьКопированиеФайла (для платформ до 8.3.18)
&НаКлиенте
Процедура СкопироватьСОповещением(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеКопирования", ЭтотОбъект);
НачатьКопированиеФайла(Оповещение, "C:\Обмен\in.xml", "C:\Архив\in.xml");
КонецПроцедуры
&НаКлиенте
Процедура ПослеКопирования(СкопированныйФайл, ДополнительныеПараметры) Экспорт
Сообщить("Готово: " + СкопированныйФайл);
КонецПроцедуры
Копирование в фоновом задании
Если нужно скопировать тысячи файлов или большой архив на сервере, вынесите работу в фоновое задание. Пользователь продолжит работать, а копирование пойдёт в отдельном сеансе.
Параметры = Новый Массив;
Параметры.Добавить("\\FS01\Обмен");
Параметры.Добавить("\\FS02\Архив\Обмен");
ФоновыеЗадания.Выполнить("РаботаСФайламиСервер.СкопироватьКаталог",
Параметры, , "Копирование каталога обмена");
Процедура должна быть экспортной и лежать в общем серверном модуле. В конфигурациях на БСП для этого удобнее модуль ДлительныеОперации: он сам показывает пользователю ожидание и возвращает результат. Для регулярного копирования по расписанию заведите регламентное задание с той же процедурой.
Фоновое задание выполняется на сервере и от имени службы 1С. Все правила о путях и правах из первого раздела действуют и здесь.
Какой метод копирования выбрать
Точные цифры скорости зависят от диска, сети и антивируса, поэтому ориентируйтесь на характер задачи, а не на замеры с чужого сервера.
| Метод | Когда подходит | Память | Скорость | Риски |
|---|---|---|---|---|
КопироватьФайл | Один файл, известные пути | Минимум | Высокая, копирует система | Нет каталогов, нет прогресса |
ДвоичныеДанные | Нужно передать или сохранить файл в базе | Весь файл в памяти | Высокая на малых файлах | Нехватка памяти на файлах в гигабайты |
ФайловыйПоток + КопироватьВ | Большие файлы на сервере | Размер буфера | Сопоставима с КопироватьФайл | Нужно не забыть закрыть потоки |
| Поток с ручным буфером | Прогресс, отмена, хеш на лету | Размер буфера | Чуть ниже из-за цикла | Ошибка с длиной последней порции |
FileSystemObject, robocopy | Папки, маски, повторы по сети | Вне 1С | Высокая | Только Windows, запрет в безопасном режиме |
КопироватьФайлАсинх | Клиентский код, веб-клиент | Минимум | Как у КопироватьФайл | Требует платформу 8.3.18 и выше |
Простое правило: один файл — КопироватьФайл или его асинхронный вариант, передача между клиентом и сервером — ДвоичныеДанные и временное хранилище, файлы больше сотни мегабайт — потоки, папки по расписанию — фоновое или регламентное задание.