Сжатие и распаковка ZIP средствами встроенного языка 1С:Предприятия 8.3
Содержание
Архивировать файлы в 1С можно без внешних программ и библиотек: в платформе 8.3 есть объект ЗаписьZipФайла для сжатия и ЧтениеZipФайла для распаковки. Три строки кода — Новый ЗаписьZipФайла(ИмяАрхива), Добавить(Путь), Записать() — и на диске готовый ZIP, который открывается любым архиватором.
Всё остальное — детали, на которых обычно и спотыкаются: где выполняется код (клиент или сервер), как собрать пути, что делать с кириллицей в именах, как отдать архив пользователю и не оставить мусор во временном каталоге. Ниже код для управляемых форм и платформы 8.3.2x, тема общая для всех конфигураций.
Какие объекты платформы работают с ZIP-архивами
Платформа поддерживает только формат ZIP. Форматы RAR и 7z встроенными средствами не создаются и не читаются.
| Объект | Для чего нужен |
|---|---|
ЗаписьZipФайла | Создать архив, добавить файлы и каталоги, задать пароль и степень сжатия |
ЧтениеZipФайла | Открыть архив, получить список элементов, извлечь всё или выборочно |
ЭлементZipФайла | Один файл внутри архива: имя, полное имя с путём, признак шифрования |
Параметры архива задаются системными перечислениями:
- 📦
МетодСжатияZIP—СжатиеилиКопирование(файлы складываются без сжатия, быстро); - 🎚️
УровеньСжатияZIP—Минимальный,Оптимальный,Максимальный; - 🔐
МетодШифрованияZIP—Zip20,AES128,AES192,AES256; - 🔤
КодировкаИменФайловВZIPФайле—Авто,UTF8,КодировкаОСДополнительноUTF8; - 📁
РежимСохраненияПутейZIP—НеСохранятьПути,СохранятьОтносительныеПути,СохранятьПолныеПути; - 🔁
РежимОбработкиПодкаталоговZIP—НеОбрабатыватьилиОбрабатыватьРекурсивно; - 📤
РежимВосстановленияПутейФайловZIP—ВосстанавливатьилиНеВосстанавливатьпри распаковке.
Точный состав параметров конструктора и методов для своей версии платформы проверяйте в синтакс-помощнике: открывается по Shift+F1 в конфигураторе, поиск по слову «Zip».
Если нужен формат, отличный от ZIP, остаётся только запуск внешнего архиватора через ЗапуститьПриложение() или КомандаСистемы(). Это работает, пока архиватор установлен на той машине, где выполняется код, и пока у пользователя есть права на запуск.
Подготовка: где выполняется код, пути и временные файлы
Прежде чем писать архивацию, решите, на какой стороне она пойдёт.
Клиент или сервер
В клиент-серверной базе сервер 1С видит свою файловую систему, а не диск пользователя. Путь C:\Users\Иванов\Desktop\Акты в серверной процедуре окажется пустым или вовсе не найдётся.
Рабочая схема для управляемой формы:
- Файлы, которые лежат у пользователя, передайте на сервер через временное хранилище (метод помещения файлов на клиенте, в типовых на БСП —
ФайловаяСистемаКлиент). - Архив собирайте в процедуре
&НаСерверево временном каталоге сервера. - Готовый архив прочитайте в
ДвоичныеДанные, положите во временное хранилище и отдайте клиенту адрес. - Временный файл архива сразу удалите.
Служба сервера 1С работает под своей учётной записью, обычно USR1CV8. Если архив пишется в сетевую папку или на другой диск сервера, права на запись нужны именно этой учётной записи, а не пользователю 1С.
Сборка путей
Чтобы код работал и на Windows, и на Linux-сервере, не вписывайте обратный слеш руками:
ПолучитьРазделительПути()вернёт\или/в зависимости от ОС;КаталогВременныхФайлов()— временный каталог текущего сеанса;ПолучитьИмяВременногоФайла("zip")— уникальное имя файла с нужным расширением;Новый Файл(Путь)с методамиСуществует()иЭтоКаталог()— проверка до начала работы;НайтиФайлы(Каталог, "*.pdf", Истина)— отбор файлов по маске, третий параметр включает поиск в подкаталогах.
&НаСервереБезКонтекста
Функция ДобавитьРазделитель(Каталог)
Разделитель = ПолучитьРазделительПути();
Если СтрЗаканчиваетсяНа(Каталог, Разделитель) Тогда
Возврат Каталог;
КонецЕсли;
Возврат Каталог + Разделитель;
КонецФункции
Маска *.* в Linux не находит файлы без расширения. Если на сервере под Linux встречаются такие файлы, используйте маску *.
Как создать ZIP-архив в 1С: пошаговый пример
Задача: упаковать каталог со всеми подкаталогами, сохранить структуру папок и отдать архив пользователю.
Шаг 1. Создаём объект записи
Конструктор принимает имя архива, пароль, комментарий, метод и уровень сжатия, метод шифрования и кодировку имён. Ненужные параметры пропускаются запятыми.
Шаг 2. Добавляем файлы
Метод Добавить() принимает путь к файлу или маску. Режим путей решает, как содержимое ляжет в архив:
| Режим | Что будет внутри архива | Когда выбирать |
|---|---|---|
НеСохранятьПути | Все файлы в корне | Набор отдельных файлов, имена уникальны |
СохранятьОтносительныеПути | Структура папок относительно каталога из маски | Упаковка каталога целиком |
СохранятьПолныеПути | Путь от корня диска | Почти никогда: получатель увидит структуру вашего сервера |
Шаг 3. Записываем и забираем результат
Пока не вызван Записать(), архива на диске нет. После записи файл можно читать.
&НаСервере
Функция СоздатьАрхивКаталогаНаСервере(КаталогИсточник)
ИмяАрхива = ПолучитьИмяВременногоФайла("zip");
Архив = Новый ЗаписьZipФайла(ИмяАрхива, , "Выгрузка документов",
МетодСжатияZIP.Сжатие,
УровеньСжатияZIP.Оптимальный, ,
КодировкаИменФайловВZIPФайле.UTF8);
Архив.Добавить(ДобавитьРазделитель(КаталогИсточник) + "*",
РежимСохраненияПутейZIP.СохранятьОтносительныеПути,
РежимОбработкиПодкаталоговZIP.ОбрабатыватьРекурсивно);
Архив.Записать();
Данные = Новый ДвоичныеДанные(ИмяАрхива);
УдалитьФайлы(ИмяАрхива);
Возврат ПоместитьВоВременноеХранилище(Данные, УникальныйИдентификатор);
КонецФункции
На клиенте полученный адрес передают в метод сохранения файла. В конфигурациях на БСП это ФайловаяСистемаКлиент.СохранитьФайл, он сам откроет диалог выбора места.
Выборочная упаковка
Если в архив нужны не все файлы, а, например, только PDF больше нуля байт, отберите их сами и добавляйте по одному:
Найденные = НайтиФайлы(КаталогИсточник, "*.pdf", Истина);
Для Каждого НайденныйФайл Из Найденные Цикл
Если НайденныйФайл.ЭтоКаталог() Или НайденныйФайл.Размер() = 0 Тогда
Продолжить;
КонецЕсли;
Архив.Добавить(НайденныйФайл.ПолноеИмя, РежимСохраненияПутейZIP.НеСохранятьПути);
КонецЦикла;
Архив.Записать();
При НеСохранятьПути два файла акт.pdf из разных папок столкнутся по имени. Если такое возможно, сначала скопируйте файлы во временный каталог под уникальными именами и упакуйте его.
Пароль и шифрование
Пароль задаётся вторым параметром конструктора, метод шифрования — шестым.
- 🔓
Zip20откроет любой архиватор, включая проводник Windows, но защита слабая. - 🔒
AES256надёжнее, однако встроенный в Windows распаковщик такие архивы не открывает. Получателю понадобится 7-Zip или аналог.
Не храните пароль в тексте модуля. Берите его из константы с ограниченными правами или передавайте параметром.
Кириллица в именах файлов
Если получатель видит в архиве нечитаемые имена, дело в кодировке. UTF8 правильно читают современные архиваторы. Для старых программ и для пользователей, которые открывают ZIP проводником, попробуйте КодировкаОСДополнительноUTF8: имя пишется в кодировке ОС и дополнительно в UTF-8.
Распаковка и чтение содержимого ZIP
ЧтениеZipФайла открывает архив и даёт коллекцию Элементы. Перебором можно посмотреть состав, не распаковывая ничего на диск.
&НаСервереБезКонтекста
Функция РаспаковатьXMLИзАрхива(ДанныеАрхива, Пароль = "")
ИмяАрхива = ПолучитьИмяВременногоФайла("zip");
ДанныеАрхива.Записать(ИмяАрхива);
КаталогРаспаковки = ДобавитьРазделитель(ПолучитьИмяВременногоФайла(""));
СоздатьКаталог(КаталогРаспаковки);
Чтение = Новый ЧтениеZipФайла(ИмяАрхива, Пароль);
Извлеченные = Новый Массив;
Для Каждого Элемент Из Чтение.Элементы Цикл
Если СтрЗаканчиваетсяНа(НРег(Элемент.Имя), ".xml") Тогда
Чтение.Извлечь(Элемент, КаталогРаспаковки,
РежимВосстановленияПутейФайловZIP.НеВосстанавливать);
Извлеченные.Добавить(КаталогРаспаковки + Элемент.Имя);
КонецЕсли;
КонецЦикла;
Чтение.Закрыть();
УдалитьФайлы(ИмяАрхива);
Возврат Извлеченные;
КонецФункции
Что здесь важно:
Элемент.Имя— только имя файла,Элемент.ПолноеИмя— путь внутри архива. Если в коллекции окажутся записи каталогов, проверка расширения их отсечёт.ИзвлечьВсе(Каталог, РежимВосстановленияПутейФайловZIP.Восстанавливать)распакует архив целиком со структурой папок.Закрыть()вызывайте до удаления файла архива. Пока объект чтения держит файл,УдалитьФайлы()его не удалит.- У элемента есть признак
Зашифрован. По нему видно, нужен ли пароль, ещё до попытки извлечения.
Архив от внешнего контрагента может содержать имена вида ..\..\файл. Распаковывайте только в отдельный временный каталог, а не в рабочие папки сервера, и проверяйте ПолноеИмя элементов перед ИзвлечьВсе().
После обработки удалите и каталог распаковки: УдалитьФайлы(КаталогРаспаковки).
Архивация в памяти: потоки и двоичные данные
Временные файлы на диске — самый совместимый путь, но не единственный. С 8.3.9 в платформе есть потоки: ПотокВПамяти, ФайловыйПоток, методы ОткрытьПотокДляЧтения() у двоичных данных.
В актуальных версиях 8.3 ЧтениеZipФайла принимает поток вместо имени файла. Это позволяет прочитать состав архива из присоединённого файла или из ответа HTTP-сервиса без записи на диск:
Поток = ДанныеАрхива.ОткрытьПотокДляЧтения();
Чтение = Новый ЧтениеZipФайла(Поток);
Для Каждого Элемент Из Чтение.Элементы Цикл
// проверка состава архива
КонецЦикла;
Чтение.Закрыть();
Поток.Закрыть();
Извлечение всё равно пишет файлы в каталог, поэтому для распаковки диск нужен в любом случае.
Как не упереться в память сервера:
- 🧠
ДвоичныеДанныецеликом лежат в оперативной памяти рабочего процесса. Архив на 2 ГБ, прочитанный так, съест 2 ГБ и больше. - 🗂️ Для больших объёмов работайте с файлами на диске и не читайте архив в
ДвоичныеДанныебез нужды. - 📏 Временное хранилище тоже держит данные в памяти. Большие архивы лучше отдавать частями или выкладывать в общую папку.
- 🧹 Каждый открытый поток закрывайте методом
Закрыть(), иначе файл останется заблокированным до конца сеанса.
Если архив отправляется по HTTP или почтой, размер обычно ограничен с той стороны. Проверьте Размер() у полученных двоичных данных до отправки и разбейте выгрузку на несколько архивов, если лимит превышен.
Архивация файлов по расписанию
Частая практическая задача — раз в сутки упаковывать файлы обмена, выгрузки или сканы за прошлый день и складывать архив в отдельную папку. Для этого подходит регламентное задание с процедурой в общем модуле на сервере.
Порядок такой:
- В конфигураторе создайте общий модуль с флагом «Сервер» и экспортную процедуру архивации.
- Добавьте регламентное задание и укажите в нём эту процедуру как метод.
- Настройте расписание, например ежедневно в 02:00, когда пользователей в базе мало.
- В процедуре отберите файлы через
НайтиФайлы(), упакуйте и только после успешногоЗаписать()удаляйте исходники.
Имя архива удобно строить от даты, чтобы архивы не перезаписывали друг друга:
ИмяАрхива = ДобавитьРазделитель(КаталогАрхивов)
+ "obmen_" + Формат(ТекущаяДатаСеанса() - 86400, "ДФ=yyyyMMdd") + ".zip";
При запуске 7 октября 2026 года получится obmen_20261006.zip — архив за предыдущие сутки.
Регламентное задание выполняется от имени службы сервера 1С, а не от пользователя. Каталог архивов должен быть доступен на запись учётной записи службы, иначе задание будет падать с ошибкой доступа каждую ночь.
Старые архивы тоже стоит чистить: отберите их по маске obmen_*.zip и удаляйте те, у которых ПолучитьВремяИзменения() объекта Файл старше нужного срока хранения, например 90 дней.
Обработка ошибок при сжатии и распаковке
Работа с файлами — место, где ошибки возникают не по вине кода: файл занят, места нет, пароль неверный. Оборачивайте архивацию в Попытка … Исключение и убирайте временные файлы в обеих ветках.
ИмяАрхива = ПолучитьИмяВременногоФайла("zip");
Попытка
Архив = Новый ЗаписьZipФайла(ИмяАрхива);
Архив.Добавить(ПутьКФайлу, РежимСохраненияПутейZIP.НеСохранятьПути);
Архив.Записать();
Данные = Новый ДвоичныеДанные(ИмяАрхива);
УдалитьФайлы(ИмяАрхива);
Исключение
Информация = ИнформацияОбОшибке();
ЗаписьЖурналаРегистрации("Архивация файлов", УровеньЖурналаРегистрации.Ошибка, , ,
ПодробноеПредставлениеОшибки(Информация));
Если Новый Файл(ИмяАрхива).Существует() Тогда
УдалитьФайлы(ИмяАрхива);
КонецЕсли;
ВызватьИсключение КраткоеПредставлениеОшибки(Информация);
КонецПопытки;
Почему так:
- в журнал регистрации уходит подробный текст со стеком, администратор найдёт его по событию «Архивация файлов»;
- пользователю показывается короткое сообщение без технических деталей;
- недописанный архив не остаётся во временном каталоге сервера.
Проверку Новый Файл(...).Существует() делайте до вызова Добавить() для каждого исходного файла. Сообщение «файл не найден» понятнее, чем исключение из глубины платформы.
*.* на Linux пропускает файлы без расширения. Проверьте, где выполняется процедура, и замените маску на *.Зашифрован у элементов и передайте пароль в конструктор ЧтениеZipФайла.Закрыть() перед УдалитьФайлы().Zip20 или попросите получателя открыть архив в 7-Zip.КодировкаОСДополнительноUTF8.Частые вопросы
Можно ли добавить файл в уже существующий ZIP-архив?
ЗаписьZipФайла всегда создаёт архив заново. Чтобы дополнить архив, распакуйте его во временный каталог, положите туда новые файлы и упакуйте заново.Работает ли архивация в веб-клиенте?
Как заархивировать файлы из присоединённых файлов документа?
НеСохранятьПути.Насколько сжимаются файлы?
МетодСжатияZIP.Копирование, это быстрее.