Двоичные файлы повреждаются при распаковке с помощью libzip

Мне нужно было разархивировать файлы .zip в моем проекте Qt. Поэтому я установил libzip. Я написал функцию для распаковки ZIP-файла с учетом его местоположения и пути к целевому каталогу. Функция может корректно распаковывать простые текстовые файлы (например, .json, .txt и другие), но всегда повреждает двоичные файлы любого типа, такие как PNG или MP4. Вот функция, которую я написал:

void Converter::unzipFile(QString srcFilePath_, QString destinationDirectoryPath_) {
    std::string srcFilePath = srcFilePath_.toStdString();
    std::string destinationDirectoryPath = destinationDirectoryPath_.toStdString();

    char bufferStr[100];
    int error = 0;
    int fileHandle;

    struct zip *zipArchive = zip_open(srcFilePath.c_str(), 0, &error);
    struct zip_file *zippedFile;
    struct zip_stat zippedFileStats;

    if (not QDir().exists(destinationDirectoryPath_)) {
        QDir().mkpath(destinationDirectoryPath_);
    }

    if (zipArchive == NULL) {
        zip_error_to_str(bufferStr, sizeof(bufferStr), error, errno);
        std::cout << "[ERROR] Can not open the zip file: " << error <<  ":\n" << bufferStr;
    }

    for (int index = 0; index < zip_get_num_entries(zipArchive, 0); index++) {
        if (zip_stat_index(zipArchive, index, 0, &zippedFileStats) == 0) {
            int zipFileNameLength = strlen(zippedFileStats.name);

            if (zippedFileStats.name[zipFileNameLength - 1] == '/') {  // i.e. folder
                QDir().mkpath(destinationDirectoryPath_ + "/" + zippedFileStats.name);
            } else {  // i.e. file
                zippedFile = zip_fopen_index(zipArchive, index, 0);
                if (zippedFile == NULL) {
                    qDebug() << "[ERROR] Can not open the file in zip archive.";
                    continue;
                }

                fileHandle = open((destinationDirectoryPath + "/" + zippedFileStats.name).c_str(), O_RDWR | O_TRUNC | O_CREAT, 0644);
                if (fileHandle < 0) {
                    qDebug() << "[ERROR] Can not create the file (into which zipped data is to be extracted).";
                    continue;
                }

                int totalFileDataLength = 0;
                while (totalFileDataLength != (long long) zippedFileStats.size) {
                    int fileDataLength = zip_fread(zippedFile, bufferStr, 100);

                    if (fileDataLength < 0) {
                        qDebug() << "[ERROR] Can not read the zipped file.";
                        exit(1);
                    }

                    write(fileHandle, bufferStr, fileDataLength);
                    totalFileDataLength += fileDataLength;
                }

                close(fileHandle);
                zip_fclose(zippedFile);
            }
        } else {
            qDebug() << "IDK what is here 🫥.";
        }
    }

    if (zip_close(zipArchive) == -1) {
        qDebug() << "[ERROR] Cannot close the zip file.";
    }
}

ПРИМЕЧАНИЕ. Все, что связано с Qt, работает, я проверил все переменные и убедился, что они имеют правильные значения пути. Обычные текстовые файлы легко распаковываются. Эта проблема только с бинарными файлами.

ПРИМЕЧАНИЕ. Я перепробовал все соответствующие решения в Интернете, но ничего не помогло, поэтому этот вопрос не является дубликатом.

Что мне делать? Любая помощь будет оценена.

Проверьте режим, в котором вы открываете файл для записи — есть ли какие-то особые флаги, которые могут вам понадобиться для включения двоичной записи? («wb»/ios::binary?)

FreudianSlip 27.11.2022 07:37

@FreudianSlip используемые функции (open(), write()) не имеют отдельных текстовых/двоичных режимов. write() записывает только необработанные байты. Однако рассматриваемый код явно C++, поэтому вместо специфичных для платформы функций std::ofstream/ios::binary следует использовать стандартный класс open() в режиме write ().

Remy Lebeau 27.11.2022 07:50

@ManbirJudge функции, которые вы используете для распаковки отдельных файлов и записи их на диск, работают только с необработанными байтами, и AFAICS вы правильно обрабатываете эти байты. Как ТОЧНО повреждаются бинарные файлы? Пожалуйста, будьте более конкретными. Сравните необработанные байты вашего вывода с необработанными байтами, полученными другими инструментами распаковки, в чем между ними разница?

Remy Lebeau 27.11.2022 07:58

@RemyLebeau Это отчет о различиях между PNG-файлом, разархивированным WinRAR, и этой функцией: Отчет о различиях.

Manbir Judge 27.11.2022 08:33
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
145
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вместо этого:

            int totalFileDataLength = 0;
            while (totalFileDataLength != (long long) zippedFileStats.size) {
                int fileDataLength = zip_fread(zippedFile, bufferStr, 100);

Этот:

            zip_uint64_t totalFileDataLength = 0;
            while (totalFileDataLength != zippedFileStats.size) {
                zip_int64_t fileDataLength = zip_fread(zippedFile, bufferStr, 100);

Я не уверен, что это ваша ошибка, но без вышеуказанного изменения у вас будет проблема для любого файла размером более 2 ГБ. Это может привести к бесконечному циклу или усеченному файлу. Поскольку write ожидает параметр size_t (который может быть 32-битным или 64-битным), вы можете безопасно выполнить приведение, если компилятор жалуется:

write(fileHandle, bufferStr, (size_t)fileDataLength);

Обновлять

Вам нужно выйти из цикла, когда zip_fread возвращает ноль. Обновим весь цикл:

            while (true) {
                zip_int64_t fileDataLength = zip_fread(zippedFile, bufferStr, 100);
                if (fileDataLength == 0) {
                   break; // end of file
                }

                if (fileDataLength < 0) {
                    qDebug() << "[ERROR] Can not read the zipped file.";
                    exit(1);
                }

                write(fileHandle, bufferStr, (size_t)fileDataLength);
            }

Окончательное обновление

Windows нужно, чтобы вы передали O_BINARY или (_O_BINARY) в открытый вызов.

            fileHandle = open((destinationDirectoryPath + "/" + zippedFileStats.name).c_str(), O_RDWR | O_TRUNC | O_CREAT | O_BINARY, 0644);

Другие вопросы по теме