It is mainly a recursive algorithm that serializes data into a single file, each with its relative path to the parent folder. And deserializing is just taking binary data, creating needed subfolders, and saving the file.
Download: QtFolderCompressor.zip
[FolderCompressor.h]
#ifndef FOLDERCOMPRESSOR_H #define FOLDERCOMPRESSOR_H #include <QFile> #include <QObject> #include <QDir> class FolderCompressor : public QObject { Q_OBJECT public: explicit FolderCompressor(QObject *parent = 0); //A recursive function that scans all files inside the source folder //and serializes all files in a row of file names and compressed //binary data in a single file bool compressFolder(QString sourceFolder, QString destinationFile); //A function that deserializes data from the compressed file and //creates any needed subfolders before saving the file bool decompressFolder(QString sourceFile, QString destinationFolder); private: QFile file; QDataStream dataStream; bool compress(QString sourceFolder, QString prefex); }; #endif // FOLDERCOMPRESSOR_H
[FolderCompressor.cpp]
#include "FolderCompressor.h" FolderCompressor::FolderCompressor(QObject *parent) : QObject(parent) { } bool FolderCompressor::compressFolder(QString sourceFolder, QString destinationFile) { QDir src(sourceFolder); if(!src.exists())//folder not found { return false; } file.setFileName(destinationFile); if(!file.open(QIODevice::WriteOnly))//could not open file { return false; } dataStream.setDevice(&file); bool success = compress(sourceFolder, ""); file.close(); return success; } bool FolderCompressor::compress(QString sourceFolder, QString prefex) { QDir dir(sourceFolder); if(!dir.exists()) return false; //1 - list all folders inside the current folder dir.setFilter(QDir::NoDotAndDotDot | QDir::Dirs); QFileInfoList foldersList = dir.entryInfoList(); //2 - For each folder in list: call the same function with folders' paths for(int i=0; i<foldersList.length(); i++) { QString folderName = foldersList.at(i).fileName(); QString folderPath = dir.absolutePath()+"/"+folderName; QString newPrefex = prefex+"/"+folderName; compress(folderPath, newPrefex); } //3 - List all files inside the current folder dir.setFilter(QDir::NoDotAndDotDot | QDir::Files); QFileInfoList filesList = dir.entryInfoList(); //4- For each file in list: add file path and compressed binary data for(int i=0; i<filesList.length(); i++) { QFile file(dir.absolutePath()+"/"+filesList.at(i).fileName()); if(!file.open(QIODevice::ReadOnly))//couldn't open file { return false; } dataStream << QString(prefex+"/"+filesList.at(i).fileName()); dataStream << qCompress(file.readAll()); file.close(); } return true; } bool FolderCompressor::decompressFolder(QString sourceFile, QString destinationFolder) { //validation QFile src(sourceFile); if(!src.exists()) {//file not found, to handle later return false; } QDir dir; if(!dir.mkpath(destinationFolder)) {//could not create folder return false; } file.setFileName(sourceFile); if(!file.open(QIODevice::ReadOnly)) return false; dataStream.setDevice(&file); while(!dataStream.atEnd()) { QString fileName; QByteArray data; //extract file name and data in order dataStream >> fileName >> data; //create any needed folder QString subfolder; for(int i=fileName.length()-1; i>0; i--) { if((QString(fileName.at(i)) == QString("\\")) || (QString(fileName.at(i)) == QString("/"))) { subfolder = fileName.left(i); dir.mkpath(destinationFolder+"/"+subfolder); break; } } QFile outFile(destinationFolder+"/"+fileName); if(!outFile.open(QIODevice::WriteOnly)) { file.close(); return false; } outFile.write(qUncompress(data)); outFile.close(); } file.close(); return true; }
Nice :-) Thanks a lot!!
ReplyDeleteWelcome.
DeleteTwo features that you missed. Hidden folders and files prefixed with "." and empty folders.
ReplyDeleteTo include hidden files change filters to:
dir.setFilter(QDir::NoDotAndDotDot | QDir::Hidden | QDir::Dirs);
dir.setFilter(QDir::NoDotAndDotDot | QDir::Hidden | QDir::Files);
To add empty folders:
In compress():
if(filesList.length() == 0) {
qDebug() << "Compressing folder: " << prefex << "/";
dataStream << QString(prefex + "/");
dataStream << " ";
}
In decompress():
if(fileName.endsWith("/") == false) {
...decompress code here...
} else {
dir.mkpath(destinationFolder + "/" + fileName);
}
Very nice, thank you so much!
ReplyDeleteDo you know an easy way to compress to ZIp format with this class?
ReplyDeleteI remember that you need to use a specific lib for zip format.
DeleteThanks for your answer. I expected that you had another answer :)
ReplyDeleteWith this class I can't export my compressed files to an external application, only to applications that have this class to uncompress! It is a pity...
By the way, do you have any suggestion to ZIP code in C++ classes/library to easily integrate with Qt?
Thank you very much!!!
ReplyDeleteThk u so !
ReplyDeleteI tried with above code, it works great, thanks to you for that ,
ReplyDeletebut if any file in the source directory is open in another application then compression fails
This comment has been removed by a blog administrator.
ReplyDeleteHi do you have limitations on folder size that you can compress? I have tried folder that have only one file, whose size is 4GB but destination file is only 1 KB in size.
ReplyDeleteI don't have and specific limitations. But for a file this big, it could be a memory issue because I simply read the file and add it. So, reading 4GB can be problematic :)
DeleteMaybe you can try changing the code to stream the data. Maybe it works