QT QZipReader改进,以支持大于2G的zip文件

QZipReader对ZIP文件读取非常方便好用。即使在最新版的QT 6.6.1里,仍然存在一些问题:对于大于2G的zip文件不支持。

虽然有标准zlib可调用,但包装成一个易用且功能成熟的zip解压功能库,还是有很大的工作量,也需要有一定的经验。

于是,直接找到QT的QZipReader相关的源码文件,单独做成一个Compress工具包,方便BUG调试,其次,将来再整合进7z解压等功能。

把原来的两个头文件和一个Cpp文件,整合成两个文件:QZip.h和QZip.cpp。

把QZipReader改名为ZipReader,以避免与QT自带的头文件和DLL产生冲突。

代码工程结构如下图:

代码调试过程,就不截图了。主要修改了两个方法:

1、void ZipReaderPrivate::scanFiles()

把这些变量的类型由int纠正为正确的数据型,这就是不支持超过2G的zip文件原因。

        qint64 i = 0;
    uint start_of_directory = -1;
    ushort num_dir_entries = 0;

说明原来代码还是有一些基本规范质量问题,也不知道是有意的,还是无意的,或者是发现问题了,但没有人手去分析与修改。

2、QByteArray ZipReader::fileData(const QString &fileName)

问题跟前一个方法一样,把一些变量的类型纠正为正确的数据型。

修改后的源代码:

void ZipReaderPrivate::scanFiles()

void ZipReaderPrivate::scanFiles()
{
    if (!dirtyFileTree)
        return;

    if (! (device->isOpen() || device->open(QIODevice::ReadOnly))) {
        status = ZipReader::FileOpenError;
        return;
    }

    if ((device->openMode() & QIODevice::ReadOnly) == 0) { // only read the index from readable files.
        status = ZipReader::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
    qint64 i = 0;
    uint start_of_directory = -1;
    ushort num_dir_entries = 0;
    EndOfDirectory eod;
    while (true) {
        const qint64 pos = device->size() - qint64(sizeof(EndOfDirectory)) - i;
        if (pos < 0 || i > 65535) {
            debugx("Zip: EndOfDirectory not found。 ");
            return;
        }

        device->seek(pos);
        device->read((char *)&eod, sizeof(EndOfDirectory));
        if (readUInt(eod.signature) == 0x06054b50)
            break;
        ++i;
    }

    // have the eod
    start_of_directory = readUInt(eod.dir_start_offset);
    num_dir_entries = readUShort(eod.num_dir_entries);
    debugx("start_of_directory at %u, num_dir_entries=%d", start_of_directory, num_dir_entries);
    int comment_length = readUShort(eod.comment_length);
    if (comment_length != i)
        debugx("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;
        auto read = device->read((char *) &header.h, sizeof(CentralFileHeader));
        if (read < (qint64)sizeof(CentralFileHeader)) {
            debugx("QZip: Failed to read complete header, index may be incomplete,read:%lld,num_dir_entries:%d",read,num_dir_entries);
            break;
        }
        if (readUInt(header.h.signature) != 0x02014b50) {
            debugx("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.size() != l) {
            debugx("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.size() != l) {
            debugx("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.size() != l) {
            debugx("QZip: Failed to read read file comment, index may be incomplete");
            break;
        }

      //  debugx("found file '%s'", header.file_name.data());
        fileHeaders.append(header);
    }
}

QByteArray ZipReader::fileData(const QString &fileName)

QByteArray ZipReader::fileData(const QString &fileName) const
{
    d->scanFiles();
    int i;
    for (i = 0; i < d->fileHeaders.size(); ++i) {
        if (QString::fromLocal8Bit(d->fileHeaders.at(i).file_name) == fileName)
            break;
    }
    if (i == d->fileHeaders.size())
        return QByteArray();

    FileHeader header = d->fileHeaders.at(i);

    ushort version_needed = readUShort(header.h.version_needed);
    if (version_needed > ZIP_VERSION) {
        debugx("QZip: .ZIP specification version %d implementationis needed to extract the data.", version_needed);
        return QByteArray();
    }

    ushort general_purpose_bits = readUShort(header.h.general_purpose_bits);
    uint compressed_size = readUInt(header.h.compressed_size);
    uint uncompressed_size = readUInt(header.h.uncompressed_size);
    uint start = readUInt(header.h.offset_local_header);
    //qDebug("uncompressing file %d: local header at %d", i, start);

    d->device->seek(start);
    LocalFileHeader lh;
    d->device->read((char *)&lh, sizeof(LocalFileHeader));
    uint skip = readUShort(lh.file_name_length) + readUShort(lh.extra_field_length);
    d->device->seek(d->device->pos() + skip);

    ushort compression_method = readUShort(lh.compression_method);
    //qDebug("file=%s: compressed_size=%d, uncompressed_size=%d", fileName.toLocal8Bit().data(), compressed_size, uncompressed_size);

    if ((general_purpose_bits & Encrypted) != 0) {
        debugx("QZip: Unsupported encryption method is needed to extract the data.");
        return QByteArray();
    }

    //qDebug("file at %lld", d->device->pos());
    QByteArray compressed = d->device->read(compressed_size);
    if (compression_method == CompressionMethodStored) {
        // no compression
        compressed.truncate(uncompressed_size);
        return compressed;
    } else if (compression_method == CompressionMethodDeflated) {
        // Deflate
        //qDebug("compressed=%d", compressed.size());
        compressed.truncate(compressed_size);
        QByteArray baunzip;
        ulong len = qMax(uncompressed_size,  1u);
        int res;
        do {
            baunzip.resize(len);
            res = inflate((uchar*)baunzip.data(), &len,
                          (const uchar*)compressed.constData(), compressed_size);

            switch (res) {
            case Z_OK:
                if ((int)len != baunzip.size())
                    baunzip.resize(len);
                break;
            case Z_MEM_ERROR:
                debugx("QZip: Z_MEM_ERROR: Not enough memory");
                break;
            case Z_BUF_ERROR:
                len *= 2;
                break;
            case Z_DATA_ERROR:
                debugx("QZip: Z_DATA_ERROR: Input data is corrupted");
                break;
            }
        } while (res == Z_BUF_ERROR);
        return baunzip;
    }

    debugx("Zip: Unsupported compression method %d is needed to extract the data.", compression_method);
    return QByteArray();
}

调用示例:(功能,直接读取zip包里的指定的图像文件的内容)

Mat readImage(QString filePath,bool isZip, int flags)
{
    if(isZip)
    {
       auto index= filePath.indexOf(".zip");
       QString zipFile=filePath.left(index+4);
       QString imgName=filePath.right(filePath.length()-index-5);
 //      debugx2(zipFile<<"\n"<<imgName);
       ZipReader zipreader(zipFile);
        for(auto fileInfo : zipreader.fileInfoList()){
            if(fileInfo.isFile){
                if(fileInfo.filePath==imgName)
                {
                    debugx2(fileInfo.filePath);
                    //注意编码问题
//                    QByteArray dt = fileInfo.filePath.toUtf8();
//                    QString strtemp = QString::fromLocal8Bit(dt);
                    QByteArray array = zipreader.fileData(fileInfo.filePath);
                 //   debugx2(array.size());

                 //   imshow("image",image);
                    zipreader.close();
                    if(array.size()>0)
                    return  imdecode(std::vector<char>(array.constData(),array.constData()+array.size()),flags);
                    else
                    {
                       debugx2(zipFile<<" read fail: "<<imgName);
                        return Mat();
                    }
                }

            }

        }
        zipreader.close();
        return Mat();
    }
    Mat src = cv::imread(filePath.toLocal8Bit().data(),flags);
    return src;
}

插件运行效果:不用解压zip,就可以直接浏览zip包里的图片。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值