Введение
Qt Core не предоставляет встроенного публичного API для работы с ZIP-архивами в полном объёме — это удивляет многих разработчиков, ожидающих найти QZipFile рядом с QFile. Тем не менее, существует несколько рабочих путей: приватный класс Qt, модуль KArchive, сторонняя библиотека QuaZip и интеграция с системными утилитами. Разберём каждый вариант.
Концепция
QZipReader/QZipWriter— приватные классы, входящие в состав Qt (используются, например, для распаковки.qm-файлов и пакетов помощи). Они доступны через приватный заголовок<private/qzipreader_p.h>и<private/qzipwriter_p.h>, но Qt не гарантирует стабильность их API между версиями — использовать в продакшене не рекомендуется.
- QuaZip — самая популярная сторонняя библиотека с открытым исходным кодом, построенная на zlib и предоставляющая Qt-дружественный интерфейс (
QuaZip,QuaZipFile), совместимый сQIODevice.
- KArchive (KDE Frameworks) — мощная библиотека, поддерживающая ZIP, TAR, 7z и другие форматы, но тянет зависимость от KDE Frameworks.
- Вызов внешних утилит через
QProcess(zip/unzip,7z) — простой, но платформо-зависимый способ.
Рассмотрим практический пример на QuaZip, поскольку это наиболее распространённое и поддерживаемое Qt-ориентированное решение.
Пример кода: Архивация и распаковка ZIP в Qt
#include <QuaZip/quazip.h>
#include <QuaZip/quazipfile.h>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QDebug>
bool addFileToZip(QuaZip &zip, const QString &filePath, const QString &zipEntryName)
{
QFile inFile(filePath);
if (!inFile.open(QIODevice::ReadOnly))
return false;
QuaZipFile outFile(&zip);
QuaZipNewInfo newInfo(zipEntryName, filePath);
if (!outFile.open(QIODevice::WriteOnly, newInfo)) {
inFile.close();
return false;
}
outFile.write(inFile.readAll());
outFile.close();
inFile.close();
return outFile.getZipError() == UNZ_OK;
}
bool createZipArchive(const QString &zipPath, const QStringList &sourceFiles)
{
QuaZip zip(zipPath);
if (!zip.open(QuaZip::mdCreate)) {
qWarning() << "Не удалось создать архив:" << zipPath;
return false;
}
for (const QString &filePath : sourceFiles) {
QFileInfo info(filePath);
if (!addFileToZip(zip, filePath, info.fileName())) {
qWarning() << "Ошибка добавления файла:" << filePath;
zip.close();
return false;
}
}
zip.close();
return zip.getZipError() == UNZ_OK;
}
bool extractZipArchive(const QString &zipPath, const QString &outputDir)
{
QuaZip zip(zipPath);
if (!zip.open(QuaZip::mdUnzip)) {
qWarning() << "Не удалось открыть архив:" << zipPath;
return false;
}
QDir().mkpath(outputDir);
for (bool more = zip.goToFirstFile(); more; more = zip.goToNextFile()) {
QuaZipFile inFile(&zip);
if (!inFile.open(QIODevice::ReadOnly))
continue;
QString entryName = zip.getCurrentFileName();
QString outPath = outputDir + "/" + entryName;
QFileInfo outInfo(outPath);
QDir().mkpath(outInfo.absolutePath());
QFile outFile(outPath);
if (outFile.open(QIODevice::WriteOnly)) {
outFile.write(inFile.readAll());
outFile.close();
}
inFile.close();
}
zip.close();
return true;
}
Подключение в .pro-файле (qmake) обычно выглядит так:
QT += core
CONFIG += link_pkgconfig
PKGCONFIG += quazip1-qt6
Либо через CMake и find_package(QuaZip-Qt6 REQUIRED) с последующим target_link_libraries(myapp PRIVATE QuaZip::QuaZip).
Пояснения к коду
QuaZip представляет сам архив, а QuaZipFile — отдельную запись внутри него, реализующую интерфейс QIODevice, что позволяет читать и писать данные так же, как с обычным QFile. Режим QuaZip::mdCreate создаёт новый архив, mdUnzip — открывает для чтения существующий.
При распаковке метод goToFirstFile()/goToNextFile() итерируют по записям архива, а getCurrentFileName() возвращает относительный путь внутри ZIP, который нужно аккуратно подставить относительно целевой директории распаковки.
Подводные камни
- Атаки через Zip Slip. При распаковке нельзя слепо доверять путям внутри архива — запись может содержать
../../etc/passwd. Перед записью обязательно нормализуйте путь и проверяйте, что результат остаётся внутри целевой директории.
- Кодировки имён файлов. Старые ZIP-архивы могут хранить имена в кодировках, отличных от UTF-8 (например, CP866 для архивов, созданных на Windows с русской локалью). QuaZip поддерживает указание кодировки, но это нужно делать явно.
- Лицензирование и сборка. QuaZip и KArchive — внешние зависимости, которые нужно собрать или установить через пакетный менеджер (vcpkg, conan, apt). Это усложняет процесс сборки на CI по сравнению с использованием только Qt Core.
- Производительность для очень больших архивов. Чтение всего файла через
readAll()при добавлении в архив (как в примере) подходит для небольших файлов; для крупных стоит писать блоками черезread()/write()в цикле.
- Приватные классы Qt нестабильны. Использование
QZipReader/QZipWriterнапрямую может привести к поломке сборки при обновлении версии Qt, так как это не публичный API.
Для большинства приложений на Qt оптимальный баланс между удобством, лицензией (LGPL) и стабильностью API даёт QuaZip — она остаётся стандартом де-факто для ZIP-операций в Qt-сообществе.
================================================================================