Распаковка ZIP-архива кодом 1С: ЧтениеZipФайла, ошибки, автоматизация
Содержание
Распаковать ZIP файл в 1С можно встроенным объектом платформы ЧтениеZipФайла: открыть архив, вызвать ИзвлечьВсе(Каталог) и закрыть. Внешние архиваторы не нужны, объект есть на сервере и в толстом и тонком клиенте 1С:Предприятия 8.3.
Типичная ситуация: банк, поставщик или государственный сервис присылает выгрузку архивом, а загрузку в базу нужно сделать без ручной работы бухгалтера. Ниже код, который работает в клиент-серверном и файловом варианте, плюс разбор подводных камней: права службы, вложенные каталоги, пароль, русские имена файлов.
Что подготовить до распаковки архива
Первое, что ломает распаковку, — путаница, где выполняется код. Серверная процедура пишет файлы на диск сервера 1С, а не на компьютер пользователя.
Где окажутся файлы
| Где выполняется код | Чей диск | Под какой учётной записью |
|---|---|---|
&НаКлиенте (тонкий/толстый клиент) | компьютер пользователя | пользователь Windows |
&НаСервере в клиент-серверной базе | сервер 1С | служба агента сервера (обычно USR1CV8, в Linux usr1cv8) |
&НаСервере в файловой базе | компьютер, где запущен клиент | пользователь Windows |
| Веб-клиент | работа с ZIP только на сервере | служба веб-сервера и сервера 1С |
Чек-лист окружения
- 📁 Каталог назначения существует или создаётся кодом через
СоздатьКаталог(). - 🔐 У учётной записи службы 1С есть права на запись в этот каталог. Сетевую папку
\\server\shareслужба под локальной учёткой не увидит. - 💾 Свободного места больше, чем суммарный распакованный размер. Его можно узнать заранее (см. раздел про защиту).
- 🧹 Для промежуточных файлов берите
КаталогВременныхФайлов()иПолучитьИмяВременногоФайла(), а не жёсткийC:\Temp.
На Linux-сервере в путях разделитель /. Собирайте пути через ПолучитьРазделительПути(), тогда код не придётся переписывать при переезде сервера.
Если обработка подключается в базу как дополнительная (через механизм дополнительных отчётов и обработок), она может работать в безопасном режиме. В нём доступ к файловой системе запрещён, пока администратор не выдаст разрешения при регистрации обработки.
Распаковка ZIP встроенными средствами 1С
Основной инструмент — ЧтениеZipФайла. Его ключевые методы:
Открыть(ИмяФайла, Пароль)— открывает архив (можно передать имя файла прямо в конструктор).ИзвлечьВсе(Каталог, РежимВосстановленияПутей)— распаковывает всё.Извлечь(Элемент, Каталог, РежимВосстановленияПутей, Пароль)— распаковывает один файл.Закрыть()— освобождает файл архива.
Свойство Элементы — коллекция содержимого архива: можно посмотреть список файлов, не распаковывая их.
Минимальный вариант на сервере
&НаСервереБезКонтекста
Функция РаспаковатьZip(ИмяАрхива, КаталогНазначения)
СоздатьКаталог(КаталогНазначения);
Архив = Новый ЧтениеZipФайла(ИмяАрхива);
Архив.ИзвлечьВсе(КаталогНазначения, РежимВосстановленияПутейФайловZIP.ВосстанавливатьПолныеПути);
Архив.Закрыть();
Возврат НайтиФайлы(КаталогНазначения, ПолучитьМаскуВсеФайлы(), Истина);
КонецФункции
Функция вернёт массив объектов Файл по всем распакованным файлам и папкам, включая вложенные.
Пользователь выбирает архив на своём компьютере
Архив с компьютера пользователя сначала передают на сервер через временное хранилище. Код формы:
&НаКлиенте
Процедура ЗагрузитьАрхив(Команда)
Оповещение = Новый ОписаниеОповещения("ПослеПомещенияФайла", ЭтотОбъект);
НачатьПомещениеФайла(Оповещение, , "", Истина, УникальныйИдентификатор);
КонецПроцедуры
&НаКлиенте
Процедура ПослеПомещенияФайла(Результат, Адрес, ВыбранноеИмяФайла, ДополнительныеПараметры) Экспорт
Если Не Результат Тогда
Возврат; // пользователь отменил выбор
КонецЕсли;
КоличествоФайлов = РаспаковатьНаСервере(Адрес);
Сообщить("Распаковано файлов: " + Формат(КоличествоФайлов, "ЧН=0; ЧГ=0"));
КонецПроцедуры
&НаСервереБезКонтекста
Функция РаспаковатьНаСервере(Адрес)
ИмяАрхива = ПолучитьИмяВременногоФайла("zip");
ПолучитьИзВременногоХранилища(Адрес).Записать(ИмяАрхива);
Каталог = ПолучитьИмяВременногоФайла("");
СоздатьКаталог(Каталог);
Архив = Новый ЧтениеZipФайла(ИмяАрхива);
Архив.ИзвлечьВсе(Каталог, РежимВосстановленияПутейФайловZIP.ВосстанавливатьПолныеПути);
Архив.Закрыть();
УдалитьФайлы(ИмяАрхива);
Найденные = НайтиФайлы(Каталог, ПолучитьМаскуВсеФайлы(), Истина);
Количество = 0;
Для Каждого ФайлАрхива Из Найденные Цикл
Если ФайлАрхива.ЭтоФайл() Тогда
Количество = Количество + 1;
// здесь обработка файла: чтение XML, загрузка в документы и т.д.
КонецЕсли;
КонецЦикла;
УдалитьФайлы(Каталог);
Возврат Количество;
КонецФункции
Этот вариант работает и в веб-клиенте: файл с машины пользователя уходит на сервер, а распаковка идёт там.
Распаковка прямо на компьютер пользователя
Иногда файлы нужны не базе, а человеку: например, распаковать присланный комплект документов в папку «Загрузки». В тонком и толстом клиенте объект ЧтениеZipФайла доступен и на клиенте, так что код можно выполнить в процедуре &НаКлиенте.
Порядок такой:
- Пользователь выбирает архив и каталог через
ДиалогВыбораФайла(режимыОткрытиеиВыборКаталога), вызванный немодально методомПоказать. - В обработчике оповещения создаётся
ЧтениеZipФайлаи вызываетсяИзвлечьВсев выбранный каталог. - Файлы пишутся с правами пользователя Windows, служба сервера 1С в этом не участвует.
В веб-клиенте так не получится: там работа с ZIP возможна только на сервере, а результат отдают пользователю через НачатьПолучениеФайлов или аналогичные методы получения файлов.
В платформе 8.3.9 и новее конструктор ЧтениеZipФайла принимает поток. Тогда временный файл архива не нужен: Новый ЧтениеZipФайла(ДвоичныеДанные.ОткрытьПотокДляЧтения()).
Вложенные папки и структура ZIP-архива
Второй параметр ИзвлечьВсе решает, что делать с папками внутри архива.
Значение РежимВосстановленияПутейФайловZIP | Результат |
|---|---|
ВосстанавливатьПолныеПути | дерево папок воссоздаётся, подкаталоги создаются автоматически |
НеВосстанавливать | все файлы складываются в один каталог |
С НеВосстанавливать есть ловушка: если в разных подпапках лежат файлы с одним именем (2024/act.xml и 2025/act.xml), последний перезапишет предыдущий. Для выгрузок с повторяющимися именами берите полные пути.
Выборочная распаковка
Часто из архива нужен один тип файлов, например только XML. Перебирать коллекцию Элементы и извлекать нужное быстрее, чем распаковать всё и чистить лишнее:
Архив = Новый ЧтениеZipФайла(ИмяАрхива);
Для Каждого Элемент Из Архив.Элементы Цикл
Если НРег(Элемент.Расширение) = "xml" Тогда
Архив.Извлечь(Элемент, Каталог, РежимВосстановленияПутейФайловZIP.НеВосстанавливать);
КонецЕсли;
КонецЦикла;
Архив.Закрыть();
У элемента полезны свойства Имя, ПолноеИмя (путь внутри архива), Путь, Расширение, НеСжатыйРазмер и Зашифрован. Расширение платформа возвращает без точки.
Мусор из архивов, созданных на macOS
Архивы, собранные на Mac, содержат служебную папку __MACOSX и файлы .DS_Store. Отсекайте их фильтром по ПолноеИмя, иначе загрузчик попытается прочитать служебный файл как XML и упадёт.
Архив внутри архива
Поставщики иногда упаковывают ZIP в ZIP. Здесь работает рекурсия: после распаковки ищем в каталоге файлы с расширением zip и для каждого вызываем ту же функцию с новым подкаталогом. Ограничьте глубину (например, 3 уровня), чтобы испорченный архив не завёл код в бесконечный цикл.
Ошибки при распаковке ZIP и защита от сбоев
Любая операция с файлами в 1С может выбросить исключение: файл занят, нет прав, архив повреждён. Распаковку всегда оборачивают в Попытка, а архив закрывают в обеих ветках, иначе файл останется заблокированным до перезапуска рабочего процесса.
Архив = Неопределено;
Попытка
Архив = Новый ЧтениеZipФайла(ИмяАрхива, Пароль);
Архив.ИзвлечьВсе(Каталог, РежимВосстановленияПутейФайловZIP.ВосстанавливатьПолныеПути);
Архив.Закрыть();
Исключение
Если Архив <> Неопределено Тогда
Архив.Закрыть();
КонецЕсли;
ЗаписьЖурналаРегистрации("Распаковка ZIP", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(ИнформацияОбОшибке()));
ВызватьИсключение;
КонецПопытки;
Запись в журнал регистрации пригодится, когда распаковка идёт фоном и пользователь сообщения не увидит.
Проверка до распаковки
Перед ИзвлечьВсе стоит пройтись по Элементы и проверить две вещи:
- Пути с
... Злонамеренный архив может содержать элемент вида../../bin/файл, который при восстановлении путей запишется за пределы каталога назначения. ЕслиСтрНайти(Элемент.ПолноеИмя, "..") > 0, распаковку прерываем. - Суммарный размер. Складываем
НеСжатыйРазмервсех элементов. Архив весом 1 МБ может разворачиваться в десятки гигабайт и забить диск сервера. Задайте разумный потолок под ваши данные, например 500 МБ, и сравнивайте с ним.
Архивы из внешних источников (почта, сайты, загрузка пользователем) распаковывайте только после этих двух проверок.
Разбор частых сбоев
Контроль результата
После распаковки полезно сверить итог с содержимым архива. Простая проверка: число элементов-файлов в Архив.Элементы равно числу файлов, которые вернул НайтиФайлы по каталогу назначения. Если числа не сходятся, часть файлов не записалась (нет места, длинный путь, запрещённые символы в имени).
На Windows отдельно помните про длину пути. Глубоко вложенная структура архива плюс длинный путь каталога назначения легко превышают 260 символов, и такие файлы не создаются. Распаковывайте в короткий каталог вроде C:\Z\ или во временный каталог службы.
Автоматизация распаковки через внешнюю обработку
Чтобы не трогать типовую конфигурацию
, распаковку оформляют внешней обработкой (.epf). Порядок такой:
- В конфигураторе:
Файл → Новый → Внешняя обработка. - Добавьте реквизиты
ПутьКАрхивуиКаталогНазначения(тип «Строка»), создайте управляемую форму. - Вынесите на форму команду «Распаковать» и вставьте код из раздела выше: клиентская часть выбирает файл, серверная распаковывает.
- Сохраните обработку и откройте её в пользовательском режиме через
Файл → Открыть.
Распаковка по расписанию
Если архивы приходят регулярно в один каталог (например, банк кладёт выписки на файловый ресурс), удобно разбирать их без человека. В типовых конфигурациях на БСП для этого есть механизм дополнительных обработок: в БП 3.0 он включается в Администрирование → Печатные формы, отчеты и обработки флажком «Дополнительные отчеты и обработки».
Обработку регистрируют как дополнительную, а для её серверной команды задают расписание. Внутри команды:
- 🔎
НайтиФайлы(КаталогВходящих, "*.zip")собирает новые архивы; - 📦 каждый распаковывается во временный каталог с проверками из прошлого раздела;
- 📄 файлы загружаются в базу;
- 🗂️ обработанный архив переносится в папку «Архив» через
ПереместитьФайл(), чтобы не разобрать его повторно.
Регламентное задание выполняется на сервере под учётной записью службы 1С. Каталог входящих должен быть доступен именно ей, а не пользователю, который настраивал обработку.
1С или 7-Zip: какой способ распаковки выбрать
Встроенный объект закрывает большинство задач. Внешний архиватор нужен, когда приходит не ZIP или когда стандартный разбор спотыкается о кодировку.
| Способ | Когда подходит | Ограничения и риски |
|---|---|---|
ЧтениеZipФайла | обычные ZIP-архивы, работа в веб-клиенте, на сервере Linux и Windows | только формат ZIP; кодировка старых архивов |
7-Zip через ЗапуститьПриложение | RAR, 7z, многотомные архивы, проблемные имена файлов | 7-Zip должен быть установлен на той машине, где выполняется код |
unzip на Linux-сервере | быстрая распаковка больших архивов на сервере | пакет нужно поставить на сервер; права пользователя usr1cv8 |
| Ручная распаковка в проводнике | разовая задача | не автоматизируется |
Вызов 7-Zip из кода
КодВозврата = 0;
Команда = """C:\Program Files\7-Zip\7z.exe"" x """ + ИмяАрхива + """ -o""" + Каталог + """ -y";
ЗапуститьПриложение(Команда, , Истина, КодВозврата);
Если КодВозврата > 1 Тогда
ВызватьИсключение "7-Zip завершился с ошибкой, код " + Формат(КодВозврата, "ЧН=0; ЧГ=0");
КонецЕсли;
Ключ x распаковывает с сохранением папок, -o задаёт каталог (пишется слитно с путём), -y отвечает «да» на перезапись. Код возврата 0 означает успех, 1 — предупреждение, 2 и выше — ошибку.
Для Linux аналог: unzip -o архив.zip -d /tmp/out.
Не подставляйте в команду имя файла, пришедшее извне, без проверки: символы & и " в имени превращают строку в другую команду. Безопаснее сначала сохранить архив под своим именем из ПолучитьИмяВременногоФайла("zip").