需求背景:
在停车收费项目中,一些车流量大的车厂日志信息文件增加的很快,虽然采用spdlog根据大小限制了每一份日志的大小,但是往往这种车流量大的车厂是一些大型商场,各种类型车辆收费不同,不同时间往往有不同的优惠,且跟车严重会带来不少各种各样的问题;日志需要保存的时间就更久一些,因此对于日志按照时间尺度进行打包压缩是一个很好的方案;此外对于软件升级工具一般需要对现场数据库数据和原版软件备份,防止升级失败影响现场使用,一般数据库数据和软件等较大,也会需要采用压缩备份。目前在数据备份等需要压缩的场景中,在备份线程中使用zlib进行压缩操作。
zlib的使用
zlib基本介绍
zlib是一个开源的数据压缩库,用于在应用程序中进行数据的压缩和解压缩操作。它提供了一组函数和数据结构,可以实现广泛的压缩算法,其中最常用的是Deflate算法。zlib库的设计简单、高效,并且具有广泛的应用领域。
zlib库的主要特点包括:
- 数据压缩和解压缩:zlib库可以将数据压缩为更小的体积,以节省存储空间或网络带宽,并且可以将压缩后的数据解压缩回原始数据。
- 高效性能:zlib库的压缩和解压缩算法经过优化,具有较高的压缩速度和解压缩速度。
- 可移植性:zlib库是使用纯C编写的,可以在各种操作系统和平台上进行编译和运行。
- 简单易用:zlib库提供了简单的API,易于集成到应用程序中,并且具有丰富的文档和示例代码。
在实际应用中,zlib库常用于压缩文件、网络传输、数据库存储、图像处理等场景。它被广泛应用于各种领域,包括Web服务器、数据压缩工具、游戏开发等。
除了基本的压缩和解压缩功能,zlib库还提供了一些高级特性,如流式压缩、字典压缩、动态压缩级别调整等,以满足不同场景下的需求。zlib是一个功能强大、高效可靠的数据压缩库,可以帮助开发者在应用程序中实现数据的压缩和解压缩功能,从而提升存储和传输效率。
zlib的编译
下载地址:GitHub - madler/zlib: A massively spiffy yet delicately unobtrusive compression library.
cmake编译后根据需求生成动态库即可。
注意事项:需要包含头文件zlib.h和zconf.h,且最好两个头文件在同一个文件夹下;项目包含目录若没有到这两个文件的目录则需要注意修改zlib.h;因为zlib.h包含了zconf.h,需要修改包含目录。
项目中使用
压缩工具类代码:定义了单例模式便于使用,此外定义压缩和解压接口,具体如下。
#pragma once
#include<string>
#include "zlib.h"
#define zibInstance zlibTool::instance()
class zlibTool
{
public:
zlibTool() = default;
~zlibTool() = default;
static zlibTool* instance();
zlibTool(const zlibTool&) = delete;
zlibTool& operator= (const zlibTool&) = delete;
bool compressFile(std::string&inFilePath, std::string&outFilePath,int method = Z_DEFAULT_COMPRESSION);
bool unCompressFile(std::string&inFilePath, std::string&outFilePath);
};
压缩工具类代码实现
单例部分代码
zlibTool* zlibTool::instance()
{
static zlibTool Instance;
return &Instance;
}
压缩函数代码:
#define CHUNK 16384
bool zlibTool::compressFile(std::string&inFilePath, std::string&outFilePath, int method)
{
std::ifstream inputFile(inFilePath, std::ios::binary);
if (!inputFile) {
cLogger("ISC_UpGrade")->error(QString("打开输入文件%1失败").arg(inFilePath.c_str()).toStdString());
return false;
}
std::ofstream outputFile(outFilePath, std::ios::binary);
if (!outputFile) {
cLogger("ISC_UpGrade")->error(QString("打开输出文件%1失败").arg(outFilePath.c_str()).toStdString());
return false;
}
// 设置压缩参数
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
// 初始化压缩流
if (deflateInit(&strm, method) != Z_OK) {
cLogger("ISC_UpGrade")->error(QString("初始化 deflate stream失败").toStdString());
return false;
}
// 读取输入文件并压缩数据
const int bufferSize = CHUNK;
std::vector<char> buffer(bufferSize);
do {
inputFile.read(buffer.data(), bufferSize);
strm.avail_in = static_cast<uInt>(inputFile.gcount());
strm.next_in = reinterpret_cast<Bytef*>(buffer.data());
do {
std::vector<char> outputBuffer(bufferSize);
strm.avail_out = bufferSize;
strm.next_out = reinterpret_cast<Bytef*>(outputBuffer.data());
// 压缩数据
int result = deflate(&strm, inputFile.eof() ? Z_FINISH : Z_NO_FLUSH);
if (result == Z_STREAM_ERROR) {
cLogger("ISC_UpGrade")->error(QString("初始化 deflate stream失败").toStdString());
deflateEnd(&strm);
return false;
}
// 写入压缩后的数据到输出文件
std::streamsize compressedSize = bufferSize - strm.avail_out;
outputFile.write(outputBuffer.data(), compressedSize);
} while (strm.avail_out == 0);
} while (!inputFile.eof());
// 压缩结束
deflateEnd(&strm);
inputFile.close();
outputFile.close();
return true;
}
解压函数代码:
bool zlibTool::unCompressFile(std::string&inFilePath, std::string&outFilePath)
{
std::ifstream inputFile(inFilePath, std::ios::binary);
if (!inputFile) {
cLogger("ISC_UpGrade")->error(QString("打开输入文件%1失败").arg(inFilePath.c_str()).toStdString());
return false;
}
std::ofstream outputFile(outFilePath, std::ios::binary);
if (!outputFile) {
cLogger("ISC_UpGrade")->error(QString("打开输出文件%1失败").arg(outFilePath.c_str()).toStdString());
return false;
}
// 设置解压缩参数
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
// 初始化解压缩流
if (inflateInit(&strm) != Z_OK) {
cLogger("ISC_UpGrade")->error(QString("初始化 deflate stream失败").toStdString());
return false;
}
// 读取输入文件并解压缩数据
const int bufferSize = 16384;
std::vector<char> buffer(bufferSize);
do {
inputFile.read(buffer.data(), bufferSize);
strm.avail_in = static_cast<uInt>(inputFile.gcount());
strm.next_in = reinterpret_cast<Bytef*>(buffer.data());
do {
std::vector<char> outputBuffer(bufferSize);
strm.avail_out = bufferSize;
strm.next_out = reinterpret_cast<Bytef*>(outputBuffer.data());
// 解压缩数据
int result = inflate(&strm, inputFile.eof() ? Z_FINISH : Z_NO_FLUSH);
if (result == Z_STREAM_ERROR) {
cLogger("ISC_UpGrade")->error(QString("解压缩失败失败").arg(strm.msg).toStdString());
inflateEnd(&strm);
return false;
}
// 写入解压缩后的数据到输出文件
std::streamsize uncompressedSize = bufferSize - strm.avail_out;
outputFile.write(outputBuffer.data(), uncompressedSize);
} while (strm.avail_out == 0);
} while (!inputFile.eof());
// 解压缩结束
inflateEnd(&strm);
inputFile.close();
outputFile.close();
return true;
}
测试代码:
string filename = R"(G:\ProjectCode\2024\PmsUpdater\Code\Test.txt)";
string outFilename = R"(G:\ProjectCode\2024\PmsUpdater\Code\Test.zip)";
string outFilename2 = R"(G:\ProjectCode\2024\PmsUpdater\Code\Test2.txt)";
if (zibInstance->compressFile(filename, outFilename))
{
zibInstance->unCompressFile(outFilename, outFilename2);
}
测试结果
QZipReader/QZipWriter类
Qt私有的压缩工具,需要Qt安装时下载了源码,在Qt项目中可以很方便的使用而不必在编译第三方库。
基本用法
QZipWriter的头文件
#include <QtCore/qstring.h>
#include <QtCore/qfile.h>QT_BEGIN_NAMESPACE
class QZipWriterPrivate;
class Q_GUI_EXPORT QZipWriter
{
public:
explicit QZipWriter(const QString &fileName, QIODevice::OpenMode mode = (QIODevice::WriteOnly | QIODevice::Truncate) );explicit QZipWriter(QIODevice *device);
~QZipWriter();QIODevice* device() const;
bool isWritable() const;
bool exists() const;enum Status {
NoError,
FileWriteError,
FileOpenError,
FilePermissionsError,
FileError
};Status status() const;
enum CompressionPolicy {
AlwaysCompress,
NeverCompress,
AutoCompress
};void setCompressionPolicy(CompressionPolicy policy);//设置压缩策略
CompressionPolicy compressionPolicy() const;void setCreationPermissions(QFile::Permissions permissions);
QFile::Permissions creationPermissions() const;void addFile(const QString &fileName, const QByteArray &data);//添加待压缩的文件数据
void addFile(const QString &fileName, QIODevice *device);//添加带压缩的文件流
void addDirectory(const QString &dirName);//添加创建目录,不是压缩
void addSymLink(const QString &fileName, const QString &destination);
void close();
private:
QZipWriterPrivate *d;
Q_DISABLE_COPY(QZipWriter)
};QT_END_NAMESPACE
#endif // QT_NO_TEXTODFWRITER
#endif // QZIPWRITER_H
项目中使用:
文件夹递归压缩代码:
#include"QtGui/private/qzipreader_p.h"
#include"QtGui/private/qzipwriter_p.h"
bool ISC_AsyBackUpObj::backUp2Zip(QZipWriter *zip, const QString &dirPath, const QString &base, const QStringList&ignoreDirLists) {
QDir dir(dirPath);
QFileInfoList entries = dir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);
for (const QFileInfo &entry : entries) {
QString filePath = entry.absoluteFilePath();
if (entry.isDir())
{
if (!ignoreDirLists.contains(entry.baseName()))
{
QString baseDirStr = base.isEmpty() ? entry.fileName() : base + QDir::separator() + entry.fileName();
if (!backUp2Zip(zip, filePath, baseDirStr))
{
cLogger("ISC")->error(TR("文件夹%1压缩备份过程中打开失败").arg(filePath).toStdString().c_str());
return false;
}
}
}
else
{
// 如果是文件,则添加到ZIP
QFile file(filePath);
if (!file.open(QIODevice::ReadOnly))
{
cLogger("ISC")->error(TR("文件%1压缩备份过程中打开失败").arg(file.fileName()).toStdString().c_str());
return false;
}
else
{
QByteArray fileData = file.readAll();
QString baseName = base.isEmpty() ? entry.fileName() : base + QDir::separator() + entry.fileName();
zip->addFile(baseName, fileData);
file.close();
m_currentSize += entry.size();
}
}
}
return true;
}
递归解压代码
bool ISC_AsyBackUpObj::restoreFromZip(const QString&srcZip, const QString&destDir)
{
//解压
QZipReader zipReader(srcZip);
if (!zipReader.exists())
{
return false;
}
if (!zipReader.status() != QZipReader::Status::NoError)
{
return false;
}
//解压到目标目录 压缩文件中文件信息齐全可用
return zipReader.extractAll(destDir);
/*
bool read = zipReader.isReadable();
int count = zipReader.count();
int Status = zipReader.status();
QDir().mkpath(destDir);
foreach(const QZipReader::FileInfo &fileInfo, zipReader.fileInfoList())
{
QString extractFilePath = QDir(destDir).filePath(fileInfo.filePath);
if (fileInfo.isDir)
{
QDir().mkpath(extractFilePath);
}
else
{
QFile extractFile(extractFilePath);
if (!extractFile.open(QIODevice::WriteOnly))
{
return false;
}
QByteArray data = zipReader.fileData(fileInfo.filePath);
if (data.isEmpty())
{
continue;
}
if (extractFile.write(data) == -1)
{
return false;
}
}
}
return true;
*/
}