QT 使用 QXlsx 时遇到包含 <private/qzipreader_p.h> 和 <private/qzipreader_p.h> 错误的解决方案

前言

最近在做项目时,客户提出了一个新需求"审计日志导出",即需要给程序加上导出记录的操作日志的功能,导出为一个本地excel文件。

此时想到以前学习过一个第三方开源库QXlsx,可以用来导出excel文件的操作,遂把QXlsx库的源码直接添加到项目中,然后处理可以正常导出excel文件;

QT 使用第三方库QtXlsx操作Excel表

因为我是在Windows环境进行开发的,但实际程序运行环境是在ARM架构统信UOS系统中(以下简称:目标系统);

当将开发好的代码移植到目标系统编译时,报错了,报了一个QT私有模块gui-private的错误,还有一个包含 <private/qzipreader_p.h> 的错误;

为什么呢?

经过排查,得出结果:QXlsx源码使用到了QT的私有模块,然而我在目标系统中安装的QT是使用在线命令进行安装的(QT没有提供ARM架构的安装包;要么使用命令安装QT,要么下载源码进行编译安装),安装后是没有包含到QT的源码模块,然后QT的私有模块却是在源码中的,所以程序在编译时没有找到该模块就报错了。

既然知道了原因,那该如何解决呢?

QXlsx源码使用到了QT的私有模块中ZipReader、QZipWriter模块,即压缩和解压缩模块,因为excel(.xlsx文件)本质上也是一个zip压缩包;就可以使用网络上开源的压缩模块去替换QXlsx源码中的ZipReader、QZipWriter模块,这样就可以解决问题了。

这时,想到以前也学习过在QT中引入Quazip的源码去使用,刚好可以将以前学习过的知识都联动起来了。

QT 引入Quazip和Zlib源码工程到项目中,无需编译成库,跨平台,加密压缩,带有压缩进度


1.搭建测试项目

新建QT项目,并且在项目main.cpp路径,将QXlsx源码和Quazip源码复制到此;

(源码请在上面给出的两个博客链接中去下载,都是开源出来了的,免费下载的)

然后在项目的.pro文件中包含进来:

cpp 复制代码
# 包含QXlsx源码到项目中
include($$PWD/3rdparty/qtxlsx/xlsx/qtxlsx.pri)


# 包含Quazip源码到项目中(Quazip源码中包含了zlib源码)
DEFINES +=   QUAZIP_STATIC
include($$PWD/3rdparty/quazip/3rdparty/zlib.pri)
include($$PWD/3rdparty/quazip/quazip.pri)
include($$PWD/3rdparty/quazip/zipop/zipop.pri)

最后在main函数中输入如下代码进行测试:

main.cpp(官方案例)

cpp 复制代码
#include "widget.h"

#include <QApplication>
#include <QtCore>


#include "xlsxdocument.h"
#include "xlsxchartsheet.h"
#include "xlsxcellrange.h"
#include "xlsxchart.h"
#include "xlsxrichstring.h"
#include "xlsxworkbook.h"



int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    {
        QXlsx::Document xlsx;
        xlsx.setColumnWidth(1, 20);		// 设置第一列列宽
        
        QXlsx::Format format1;
        format1.setFontColor(QColor(Qt::red));
        format1.setFontSize(15);
        format1.setHorizontalAlignment(QXlsx::Format::AlignHCenter);
        format1.setBorderStyle(QXlsx::Format::BorderDashDotDot);
        xlsx.write("A1", "Hello Qt!", format1);
        xlsx.write("B3", 12345, format1);

        QXlsx::Format format2;
        format2.setFontBold(true);
        format2.setFontUnderline(QXlsx::Format::FontUnderlineDouble);
        format2.setFillPattern(QXlsx::Format::PatternLightUp);
        xlsx.write("C5", "=44+33", format2);
        xlsx.write("D7", true, format2);

        QXlsx::Format format3;
        format3.setFontBold(true);
        format3.setFontColor(QColor(Qt::blue));
        format3.setFontSize(20);
        xlsx.write(11, 1, "Hello Row Style");
        xlsx.write(11, 6, "Blue Color");
        xlsx.setRowFormat(11, 41, format3);

        QXlsx::Format format4;
        format4.setFontBold(true);
        format4.setFontColor(QColor(Qt::magenta));
        for (int row=21; row<=40; row++)
            for (int col=9; col<16; col++)
                xlsx.write(row, col, row+col);
        xlsx.setColumnFormat(9, 16, format4);

        xlsx.write("A5", QDate(2013, 8, 29));

        QXlsx::Format format6;
        format6.setPatternBackgroundColor(QColor(Qt::green));
        xlsx.write("A6", "Background color: green", format6);

        xlsx.saveAs("book1.xlsx");
    }


    Widget w;
    w.show();
    return a.exec();
}

编译运行后:


2.QXlsx引入Quazip

上面的案例中,QXlsx使用的还是QT私有模块去处理压缩和解压缩操作,涉及到如下四个文件:

cpp 复制代码
xlsxzipreader_p.h
xlsxzipreader.cpp

xlsxzipwriter_p
xlsxzipwriter.cpp

打开xlsxzipreader.cpp和xlsxzipwriter.cpp文件查看:

发现就使用到了#include <private/qzipreader_p.h> 和 #include <private/qzipwriter_p.h>

所以就需要修改这四个文件,去除QT的私有模块,改用Quazip;

以下直接给出代码,直接复制粘贴替换原有文件内容;

2.1 xlsxzipreader_p.h

cpp 复制代码
#ifndef QXLSX_XLSXZIPREADER_P_H
#define QXLSX_XLSXZIPREADER_P_H

#include "xlsxglobal.h"
#include <QScopedPointer>
#include <QStringList>
class QuaZip;               // 改用 QuaZip 替换 QZipReader
class QIODevice;

namespace QXlsx {

class XLSX_AUTOTEST_EXPORT ZipReader
{
public:
    explicit ZipReader(const QString &fileName);
    explicit ZipReader(QIODevice *device);
    ~ZipReader();

    bool exists() const;
    QStringList filePaths() const;
    QByteArray fileData(const QString &fileName) const;

private:
    Q_DISABLE_COPY(ZipReader)
    void init();
    QScopedPointer<QuaZip> m_reader;   // 替换为 QuaZip 指针
    QStringList m_filePaths;
};

} // namespace QXlsx

#endif // QXLSX_XLSXZIPREADER_P_H

2.2 xlsxzipreader.cpp

cpp 复制代码
#include "xlsxzipreader_p.h"
#include <QuaZip.h>        // 引入 QuaZip 头文件
#include <QuaZipFile.h>    // 引入 QuaZipFile 头文件
#include <QtCore/qvector.h>
#include <QFile>

namespace QXlsx {

// 构造函数:根据文件路径初始化
ZipReader::ZipReader(const QString &filePath)
    : m_reader(new QuaZip(filePath))
{
    init();
}

// 构造函数:根据 QIODevice 指针初始化
ZipReader::ZipReader(QIODevice *device)
    : m_reader(new QuaZip(device))
{
    init();
}

// 析构函数:关闭并清理资源
ZipReader::~ZipReader()
{
    if (m_reader && m_reader->isOpen())
        m_reader->close();
}

// 初始化函数:遍历 ZIP 文件,收集所有有效文件名
void ZipReader::init()
{
    if (!m_reader->open(QuaZip::mdUnzip)) {
        return;
    }

    for (bool more = m_reader->goToFirstFile(); more; more = m_reader->goToNextFile()) {
        QuaZipFileInfo64 info;
        if (m_reader->getCurrentFileInfo(&info)) {
            // 只记录文件条目,忽略目录(目录名以 '/' 结尾)
            if (!info.name.endsWith('/')) {
                m_filePaths.append(info.name);
            }
        }
    }
    m_reader->close();
}

// 检查 ZIP 文件是否存在且可读
bool ZipReader::exists() const
{
    return QFile::exists(m_reader->getZipName()) || m_reader->isOpen();
}

// 返回 ZIP 包内所有文件路径列表
QStringList ZipReader::filePaths() const
{
    return m_filePaths;
}

// 读取 ZIP 包内指定文件的数据
QByteArray ZipReader::fileData(const QString &fileName) const
{
    if (!m_reader->open(QuaZip::mdUnzip)) {
        return QByteArray();
    }

    if (!m_reader->setCurrentFile(fileName)) {
        m_reader->close();
        return QByteArray();
    }

    QuaZipFile file(m_reader.data());
    if (!file.open(QIODevice::ReadOnly)) {
        m_reader->close();
        return QByteArray();
    }

    QByteArray data = file.readAll();
    file.close();
    m_reader->close();
    return data;
}

} // namespace QXlsx

2.3 xlsxzipwriter_p.h

cpp 复制代码
#ifndef QXLSX_ZIPWRITER_H
#define QXLSX_ZIPWRITER_H

#include <QString>
class QIODevice;
class QuaZip;   // 前置声明 QuaZip

namespace QXlsx {

class ZipWriter
{
public:
    explicit ZipWriter(const QString &filePath);
    explicit ZipWriter(QIODevice *device);
    ~ZipWriter();

    void addFile(const QString &filePath, QIODevice *device);
    void addFile(const QString &filePath, const QByteArray &data);
    bool error() const;
    void close();

private:
    QuaZip *m_zip;   // 改用 QuaZip 指针
    bool m_error;    // 添加错误标志
};

} // namespace QXlsx

#endif // QXLSX_ZIPWRITER_H

2.4 xlsxzipwriter.cpp

cpp 复制代码
#include "xlsxzipwriter_p.h"
#include <QDebug>
#include <QuaZip.h>
#include <QuaZipFile.h>
#include <QBuffer>

namespace QXlsx {

ZipWriter::ZipWriter(const QString &filePath)
    : m_zip(new QuaZip(filePath)), m_error(false)
{
    if (!m_zip->open(QuaZip::mdCreate)) {
        m_error = true;
        qWarning() << "Failed to create zip file:" << filePath;
    }
}

ZipWriter::ZipWriter(QIODevice *device)
    : m_zip(new QuaZip(device)), m_error(false)
{
    if (!m_zip->open(QuaZip::mdCreate)) {
        m_error = true;
        qWarning() << "Failed to create zip file from device";
    }
}

ZipWriter::~ZipWriter()
{
    if (m_zip) {
        if (m_zip->isOpen())
            m_zip->close();
        delete m_zip;
    }
}

bool ZipWriter::error() const
{
    return m_error || (m_zip && m_zip->getZipError() != UNZ_OK);
}

// 从 QIODevice 添加文件
void ZipWriter::addFile(const QString &filePath, QIODevice *device)
{
    if (!device || m_error) return;

    // 1. 创建 QuaZipFile 对象,与 Zip 归档关联
    QuaZipFile zipFile(m_zip);
    
    // 2. 准备新文件的信息,主要是文件名
    QuaZipNewInfo newFileInfo(filePath);
    
    // 3. 以 WriteOnly 模式打开文件,并传入文件信息
    if (!zipFile.open(QIODevice::WriteOnly, newFileInfo)) {
        m_error = true;
        qWarning() << "Failed to open file in zip for writing:" << filePath;
        return;
    }
    
    // 4. 从传入的 device 读取数据并写入 zip 文件中
    QByteArray data = device->readAll();
    if (zipFile.write(data) != data.size()) {
        m_error = true;
        qWarning() << "Failed to write data to zip file:" << filePath;
    }
    
    // 5. 关闭文件
    zipFile.close();
}

// 从 QByteArray 添加文件
void ZipWriter::addFile(const QString &filePath, const QByteArray &data)
{
    if (m_error) return;

    // 1. 创建 QuaZipFile 对象
    QuaZipFile zipFile(m_zip);
    
    // 2. 准备新文件的信息
    QuaZipNewInfo newFileInfo(filePath);
    
    // 3. 打开文件
    if (!zipFile.open(QIODevice::WriteOnly, newFileInfo)) {
        m_error = true;
        qWarning() << "Failed to open file in zip for writing:" << filePath;
        return;
    }
    
    // 4. 直接写入 QByteArray 数据
    if (zipFile.write(data) != data.size()) {
        m_error = true;
        qWarning() << "Failed to write data to zip file:" << filePath;
    }
    
    // 5. 关闭文件
    zipFile.close();
}

void ZipWriter::close()
{
    if (m_zip && m_zip->isOpen()) {
        m_zip->close();
    }
}

} // namespace QXlsx

把这四个文件内容依次替换,即可调用Quazip去处理excel,而不是使用QT私有库去处理!

这样,在没有下载安装QT源码的电脑上,QT也可以正常编译运行了!

Quazip已经完全替换了QZipReaderQZipWriter

重新编译运行程序。

至此,问题完美解决!

相关推荐
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题】【Java基础篇】第39题:说说反射的用途及实现原理,Java获取反射(Class)的三种方法
java·开发语言·后端·python·面试
PeterLi1 小时前
踩坑实录:JRebel 启动报 Mapper 重复 ID 异常,IDEA 普通启动却正常?
java·后端
甲维斯2 小时前
搞定了!任意模型接入Claude Desktop,提前用上“Opus5.0”
后端
Gopher_HBo2 小时前
线程池之ForkJoinPool
后端
fliter2 小时前
你的网站对 AI Agent 友好吗?Cloudflare 给整个互联网打了一个分
后端
星栈2 小时前
Rust + DDD 三层架构:没有 Spring、没有 DI 容器,解耦能力一点不少
后端·github
正在走向自律3 小时前
KingbaseES 命令行工具完全指南:ksql 常用操作与技巧
后端
dllxhcjla3 小时前
Spring全套
java·后端·spring
追逐时光者4 小时前
2026 年 .NET 客户端常用 MVVM 框架推荐
后端·.net