Solved QZipReader extractAll issue
-
Hello!
I am using the old Qt -
QZipReader
class to unzip some zip file. It decompresses successfully only files. When zip file contains the directories with content, it displays thisQIODevice::write
issue:QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\7zr.exe"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\aria2c.exe"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert.sh"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert_config_linux"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert_config_macos"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\convert_ve_plugin"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\test5\qt-unified-windows-x86-3.1.1-online.exe"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\files\uup-converter-wimlib.7z"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\test\yM37erm8.jpg"): device not open QIODevice::write (QFile, "C:\Users\cobra\Downloads\Output\test\yNFatJN.jpg"): device not open
Code:
#if defined(Q_OS_WIN) # undef S_IFREG # define S_IFREG 0100000 # ifndef S_ISDIR # define S_ISDIR(x) ((x) & S_IFDIR) > 0 # endif # ifndef S_ISREG # define S_ISREG(x) ((x) & 0170000) == S_IFREG # endif # define S_IFLNK 020000 # define S_ISLNK(x) ((x) & S_IFLNK) > 0 # ifndef S_IRUSR # define S_IRUSR 0400 # endif # ifndef S_IWUSR # define S_IWUSR 0200 # endif # ifndef S_IXUSR # define S_IXUSR 0100 # endif # define S_IRGRP 0040 # define S_IWGRP 0020 # define S_IXGRP 0010 # define S_IROTH 0004 # define S_IWOTH 0002 # define S_IXOTH 0001 #endif bool QZipReader::extractAll(const QString &destinationDir) const { QDir baseDir(destinationDir); // create directories first const QVector<FileInfo> allFiles = fileInfoList(); // for (FileInfo fi : allFiles) { // if (fi.isDir) { // const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath)); // if (!baseDir.mkpath(fi.filePath)) { // return false; // } // if (!QFile::setPermissions(absPath, fi.permissions)) { // return false; // } // } // } // // set up symlinks // for (FileInfo fi : allFiles) { // const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath)); // if (fi.isSymLink) { // QString destination = QFile::decodeName(fileData(fi.filePath)); // if (destination.isEmpty()) // return false; // QFileInfo linkFi(absPath); // if (!QFile::exists(linkFi.absolutePath())) // QDir::root().mkpath(linkFi.absolutePath()); // if (!QFile::link(destination, absPath)) // return false; // /* cannot change permission of links // if (!QFile::setPermissions(absPath, fi.permissions)) // return false; // */ // } // } for (FileInfo fi : allFiles) { if (!baseDir.exists()) { baseDir.mkpath(destinationDir); } const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath)); if (fi.isDir) { baseDir.mkpath(absPath); } if (fi.isFile) { QFile f(absPath); if (!f.isOpen()) { f.open(QIODevice::WriteOnly); } f.write(fileData(fi.filePath)); f.setPermissions(fi.permissions); f.close(); } } return true; } QVector<QZipReader::FileInfo> QZipReader::fileInfoList() const { d->scanFiles(); QVector<QZipReader::FileInfo> files; for (int i = 0; i < d->fileHeaders.size(); ++i) { QZipReader::FileInfo fi; d->fillFileInfo(i, fi); files.append(fi); } return files; } void QZipReaderPrivate::scanFiles() { if (!dirtyFileTree) { return; } if (!(device->isOpen() || device->open(QIODevice::ReadOnly))) { status = QZipReader::FileOpenError; return; } if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files. status = QZipReader::FileReadError; return; } dirtyFileTree = false; uchar tmp[4]; device->read((char *)tmp, 4); if (readUInt(tmp) != 0x04034b50) { qWarning() << "QZip: not a zip file!"; return; } // find EndOfDirectory header int i = 0; int start_of_directory = -1; int num_dir_entries = 0; EndOfDirectory eod; while (start_of_directory == -1) { const int pos = device->size() - static_cast<int>(sizeof(EndOfDirectory)) - i; if (pos < 0 || i > 65535) { qWarning() << "QZip: EndOfDirectory not found"; return; } device->seek(pos); device->read((char *)&eod, sizeof(EndOfDirectory)); if (readUInt(eod.signature) == 0x06054b50) { //start_of_directory = pos; break; } ++i; } // have the eod start_of_directory = readUInt(eod.dir_start_offset); num_dir_entries = readUShort(eod.num_dir_entries); //qDebug() << "start_of_directory at: " << start_of_directory << " | num_dir_entries: " << num_dir_entries; int comment_length = readUShort(eod.comment_length); if (comment_length != i) { qWarning() << "QZip: failed to parse zip file."; } comment = device->read(qMin(comment_length, i)); device->seek(start_of_directory); for (i = 0; i < num_dir_entries; ++i) { FileHeader header; int read = device->read((char *) &header.h, sizeof(CentralFileHeader)); if (read < (int)sizeof(CentralFileHeader)) { qWarning() << "QZip: Failed to read complete header, index may be incomplete"; break; } if (readUInt(header.h.signature) != 0x02014b50) { qWarning() << "QZip: invalid header signature, index may be incomplete"; break; } int l = readUShort(header.h.file_name_length); header.file_name = device->read(l); if (header.file_name.length() != l) { qWarning() << "QZip: Failed to read filename from zip index, index may be incomplete"; break; } l = readUShort(header.h.extra_field_length); header.extra_field = device->read(l); if (header.extra_field.length() != l) { qWarning() << "QZip: Failed to read extra field in zip file, skipping file, index may be incomplete"; break; } l = readUShort(header.h.file_comment_length); header.file_comment = device->read(l); if (header.file_comment.length() != l) { qWarning() << "QZip: Failed to read read file comment, index may be incomplete"; break; } qDebug() << "Found file: " << header.file_name.data(); fileHeaders.append(header); } } void QZipPrivate::fillFileInfo(int index, QZipReader::FileInfo &fileInfo) const { FileHeader header = fileHeaders.at(index); fileInfo.filePath = QString::fromLocal8Bit(header.file_name); const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF; fileInfo.isDir = S_ISDIR(mode); fileInfo.isFile = S_ISREG(mode); qDebug() << "Mode: " << mode; qDebug() << "fileInfo.filePath: " << fileInfo.filePath << " isFile: " << fileInfo.isFile << " isDir: " << fileInfo.isDir; fileInfo.isSymLink = S_ISLNK(mode); fileInfo.permissions = modeToPermissions(mode); fileInfo.size = readUInt(header.h.uncompressed_size); fileInfo.lastModified = readMSDosDate(header.h.last_mod_file); }
Console app:
#include <QCoreApplication> #include <QDir> #include <QDebug> #include "qzipreader.h" int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qDebug() << "This is a QZip test..." << endl; QString zipFileName = "C:\\Users\\cobra\\Downloads\\22610.1_amd64_en-us_professional_00fb7ba0_convert.zip"; QString outputDirPath = "C:\\Users\\cobra\\Downloads\\Output"; QZipReader qZip(zipFileName); bool isExtracted = qZip.extractAll(outputDirPath); qDebug() << "isExtracted: " << isExtracted; qZip.close(); system("Pause"); return 0; }
I have checked that everything in zip file is treated as files even directories with content, which leads to this
QIODevice::write
issue. ThefileInfo.isDir
returnsfalse (0)
. Any ideas how to fix it? I can share moreQt
code if needed. Thank you. -
I wonder why e.g. QFile::open() returns a boolean and noone checks it just to wonder why it doesn't work later on...
-
Because it contains the directory which is not created in the local file system. For example:
uup_download_linux.sh
is successfully extracted but then goes the directory with a file:\files\7zr.exe
and it leads to this error. I need something to check for directories to create it in the file system before it extracts these files or extract the directory with a content at once. Thanks.Zip content:
This issue could be here -
fillFileInfo
method:const quint32 mode = (qFromLittleEndian<quint32>(&header.h.external_file_attributes[0]) >> 16) & 0xFFFF; fileInfo.isDir = S_ISDIR(mode);
The directories are always false.
-
I think, I got it :) I will reply a bit later. Thank you very much.
-
So, I have fixed it by adding this line:
baseDir.mkpath(QFileInfo(absPath).absoluteDir().path());
Code:
bool QZipReader::extractAll(const QString &destinationDir) const { QDir baseDir(destinationDir); if (!baseDir.exists()) { baseDir.mkpath(destinationDir); } const QVector<FileInfo> allFiles = fileInfoList(); for (FileInfo fi : allFiles) { const QString absPath = QDir::toNativeSeparators(QString("%1/%2").arg(destinationDir, fi.filePath)); baseDir.mkpath(QFileInfo(absPath).absoluteDir().path()); if (fi.isFile) { QFile f(absPath); if (!f.isOpen()) { f.open(QIODevice::WriteOnly); } f.write(fileData(fi.filePath)); f.setPermissions(fi.permissions); f.close(); } } return true; }
Now, it creates all directories and extracts files. The issue is resolved. Thank you.