KArchive
When you are storing large amounts of data, how do you archive it in a easy way from within your code? The KArchive framework provides a quick and easy way to do this from within Qt apps.
KArchive supports a wide array of formats
such as p7zip, zip and tar archives, giving you the flexibility of
choosing the formats that fit your project.
Creating and opening an archive
An archive consists of:
- a root directory
- optional directories inside the root directory
- files in any directory
KArchive::writeDir() allows you to create directories while KArchive::writeFile() allows you to create directories and files.
This is done by passing string paths: writeDir("emptydir/emptysubdir") and writeFile("subdir/subsubdir/world") will generate the following structure:
hello.zip
├── emptydir/
│ └── emptysubdir/
└── somedir/
└── subdir/
└── worldCreating an archive with the above structure is very simple:
// Create a zip archive
KZip archive(QStringLiteral("hello.zip"));
// Open our archive for writing
if (archive.open(QIODevice::WriteOnly)) {
// The archive is open, we can now write data
archive.writeDir("emptydir/emptysubdir");
archive.writeFile(QStringLiteral("somedir/subdir/world"), // File name
QByteArray("The whole world inside a hello."), // Data
0100644, // Permissions
QStringLiteral("owner"), // Owner
QStringLiteral("users")); // Group
// Don't forget to close!
archive.close();
}If you already know the path to the file, you can then open the archive as follows:
if (archive.open(QIODevice::ReadOnly)) {
const KArchiveDirectory *dir = archive.directory(); // the root directory
const KArchiveFile *file = dir->file("somedir/subdir/world");
if (!file) {
qInfo() << "File not found!";
return -1;
}
QByteArray data(file->data());
qInfo() << data; // the file contents
}💡Optimization tip
To avoid reading everything into memory in one go, we can use createDevice() instead:
std::unique_ptr<QIODevice> device(file->createDevice());
while (!device.get()->atEnd()) {
qDebug() << device->readLine();
}If you don't know the file structure of the archive beforehand, you can use KArchiveEntry, the base class for KArchiveDirectory and KArchiveFile.
This can be used to check whether an entry is a file or directory:
// This example does not represent a full-fledged implementation.
if (archive.open(QIODevice::ReadOnly)) {
const KArchiveDirectory *dir = archive.directory();
const KArchiveEntry *entry = dir->entry("somedir/subdir/world");
if (!entry) {
qInfo() << "Entry not found!";
return -1;
}
if (entry->isFile()) {
const KArchiveFile *file = static_cast<const KArchiveFile *>(entry);
QByteArray data(file->data());
qDebug() << data;
}
// ...
}Advanced usecases
Working with compressed data
In addition to supporting files, KArchive also supports reading and writing compressed data to devices such as buffers or sockets via the KCompressionDevice class, allowing developers to save bandwidth transmitting data over networks.
A quick example of the KCompressionDevice class can be summed up as:
// Open the input archive
KCompressionDevice input(&file, false, KCompressionDevice::BZip2);
input.open(QIODevice::ReadOnly);
QString outputFile = (info.completeBaseName() + QLatin1String(".gz"));
// Open the new output file
KCompressionDevice output(outputFile, KCompressionDevice::GZip);
output.open(QIODevice::WriteOnly);
while (!input.atEnd()) {
// Read and uncompress the data
QByteArray data = input.read(512);
// Write data like you would to any other QIODevice
output.write(data);
}
input.close();
output.close();