当前位置: 首页 > news >正文

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

前言

最近在做项目时遇到一个需求,需要将升级的文件压缩成zip,再进行传输;

通过网络调研,有许多方式可以实现,例如QT私有模块的ZipReader、QZipWriter;或者第三方库zlib或者libzip或者quazip等;其中libzip和quazip都是依赖于zlib;

然后,这些都需要变成库,然后再链接到QT项目中进行使用;

由于项目的特殊性,有可能需要在window,Linux和ARM三个平台上去运行,如果都编译为库去使用的话,那么需要编译三个平台,甚是麻烦,而且如果系统版本不一样,可能还不能用;

所以,现阶段有两种方案,其一是使用QT自带的压缩库,其二是使用第三方库的源码;

由此可知,使用QT自带的私有库是最方便,且网络上也有教程(文章最后也提供的操作代码);

但是,使用QT自带的私有库有几个缺点,第一是需要QT5.14以上的版本才可以使用,且在QT6中已经移除了该模块;而且需要安装QT时勾选安装QT源码才可以使用;由此,这种方式pass掉;

本人在ARM平台安装QT,是使用命令行在线安装的,QT的版本无法控制,也无法安装源码!

只能使用第二种方式了,遂在网络上寻找第三方库的源码,如何将将源码潜入到QT项目中去;

网络上教程,百分之99.99都是叫你如何编译使用的,剩下百分之0.01是需要vip和付费资源,完全没有使用源码的方式;

直到我遇到了一篇博客,里面博主就是使用Quazip+Zlib源码,用于异步压缩文件为zip格式,且博主将代码开源,正好符合我的需求,再次非常感谢该博主,链接如下:

Qt+Quazip+Zlib源码工程实现带进度同步异步加解压文件_qt 压缩zip 进度-CSDN博客

但是,该项目源码也使用到了QT的私有模块,对于我这种在ARM平台下使用命令行安装的QT,且版本没有达到5.14的我来说,无疑是晴天霹雳;

项目无法运行,报错!!!

没办法,只能自己手动去修改,这难不倒身为程序员的我!

我将代码中的私有模块去掉,将博主写的压缩解压缩代码也去掉,自己动手写了一个操作类,使用了Quazip的接口,供外部使用!(同步 压缩/解压缩)

完成后,突然想起来,还有加密压缩的需求,这该怎么办呢?

在网络上调研了一整天,没啥博客能讲明白或者能使用quazip库直接给出代码的;

直到我遇到了另一篇博客,里面有分享了一些 加密压缩多个文件 和 解压含密码的压缩文件 的案例;

通过参考这些案例,我写出来自己的加密压缩和解密压缩文件和文件夹的操作函数,经过测试可行;

再次也非常感谢该博主的分享,博文链接如下:

Qt加密压缩与解压-CSDN博客

到了这一步,项目需求的加密压缩和解密压缩基本就完成了;

但转念一想,平常事我们使用的解压缩软件时,都是带有进度条的,也就是说下解压缩时,我们应该需要知道进度;

于是乎,再次修改代码,增加了解压缩操作时,将当前进度通过信号发射出去,由调用方处理这个信号即可!

因为我实现的解压缩功能是同步的,所以,调用方在处理解压缩时,如果不希望卡屏,就得使用线程去操作;

由此写出了一个简易的解压缩zip软件,运行效果如下:

压缩成功后

解压缩和压缩文件是一样的,这里就不放出演示了!

对比一下压缩后的大小:

效果还是很明显的!


目录

前言

一、基础的解压缩

二、自定义实现解压缩

1.加密压缩

1).打开zip文件

2).获取需要压缩的文件夹内部的所有文件及子文件

3).创建一个QuaZipFile 

4).遍历获取到的所有文件列表

5).创建新的ZIP文件条目信息

6).打开ZIP文件条目

7).读取写入

8).关闭文件

9).详细代码

2.解密解压缩

1).创建文件夹

 2).打开zip文件

3).获取zip内的所有文件条目

4).遍历这个链表

5).处理文件夹

 6).创建一个QuaZipFile 

7).打开QuaZipFile

8).读取写入(解压缩)

 9).关闭文件

10).详细代码

三、源码分享

1.下载后如何引入自己的项目工程中?

1).拷贝quazip文件夹到自己的项目工程中

2).在自己的项目的.pro文件引入如下代码

3).包含头文件

2.QT自带压缩库操作分享 ZipReader、QZipWriter

1).导入私有模块

2).zipcompress.h

3).zipcompress.cpp

四、结束语


一、基础的解压缩

网络上介绍Quazip库使用的,基本都是以下这些:

JlCompress mJlCompress;// 压缩文件
mJlCompress.compressFiles(fileCompressed,files);// 解压文件夹
mJlCompress.extractDir(fileCompressed, dir);// 压缩文件夹
mJlCompress.compressDir(fileCompressed, dir, recursive);// 解压文件
mJlCompress.extractFiles(fileCompressed, files);

其实如果是基础用法,上面这几句代码也够用了;

但是如果涉及到加密压缩和进度方面,就得自己去实现解压缩的过程了,且这样也更加灵活一点!

二、自定义实现解压缩

Quazip库还提供了QuaZipQuaZipFileQuaZipNewInfoQuaZipFileInfo

等类供我们自己去处理!

QuaZip 类用于打开一个.zip文件;

QuaZipFile 类用于处理zip内部的文件,即读取zip内部文件数据(解压)或者往zip内部写入文件(压缩);【在此可以进行加密和解密处理】

QuaZipNewInfo 类用于定义一个zip的文件信息,在压缩时使用;

QuaZipFileInfo 类用于处理一个zip内部的文件;

当然,还有以下这么多:(别家复制过来的)

说明
JlCompress典型操作工具类
QuaAdler32Adler32算法校验和
QuaChecksum32校验和接口
QuaCrc32CRC32校验和
QuaGzipFileGZIP 文件操作
QuaZIODevice压缩/解压 QIODevice
QuaZipZIP 文件
QuaZipDirZIP文件内目录导航
QuaZipFileZIP文件内的文件
QuaZipFileInfoZIP压缩包内的文件信息
QuaZipFilePrivateQuaZip的接口
QuaZipNewInfo被创建的文件信息
QuaZipPrivateQuaZIP内部类

下面是加密压缩和解密压缩的过程分析;

1.加密压缩

这里处理的是压缩文件夹,压缩文件与之类似;

压缩后的文件结构与原来一致;

1).打开zip文件

QuaZip newZip(fileCompressed); // 要生成的zip文件                         
if (!newZip.open(QuaZip::mdCreate)) {                               qDebug() << "Failed to create ZIP file:" << fileCompressed;                                 return false;                                                  
}

参数枚举介绍:

enum Mode {mdNotOpen,        ///< ZIP文件未打开。这是初始模式。mdUnzip,          ///< ZIP文件已打开,用于读取其内部的文件。mdCreate,         ///< ZIP文件已通过open()调用创建。mdAppend,         ///< ZIP文件以追加模式打开。这指的是ZIP/UNZIP包中的APPEND_STATUS_CREATEAFTER模式,///  意味着zip被追加到某个已存在的文件中,该文件包含自解压代码。这显然不是您想要用来///  向现有的ZIP归档添加文件的模式。mdAdd             ///< ZIP文件已打开,用于向归档中添加文件。
};

一般来说,我们只需要用到 mdUnzip 用于解压缩 和 mdCreate 用于压缩即可!

2).获取需要压缩的文件夹内部的所有文件及子文件

QStringList filePathList = 获取所有文件();

3).创建一个QuaZipFile 

QuaZipFile zipFile(&newZip);

使用QuaZip作为参数;

4).遍历获取到的所有文件列表

在循环体内部处理压缩事项;以下操作都是在循环体内部进行;

5).创建新的ZIP文件条目信息

QuaZipNewInfo info(文件名, 文件路径+文件名);

6).打开ZIP文件条目

if (!zipFile.open(QIODevice::WriteOnly, info, password.toUtf8().constData(), 0, 8)) {  qDebug() << "Failed to open ZIP entry for writing:" << fileName;                            // 关闭ZIP                                                                                    newZip.close();                                                                                                                                return false;                                                                              
}

open参数介绍:

bool open(OpenMode mode,               // 打开模式,指定是以什么方式打开文件(例如,只写、追加等)const QuaZipNewInfo& info,   // 文件信息,包括文件名、时间戳等const char *password = nullptr, // 密码,用于加密文件,如果不需要加密则传递nullptrquint32 crc = 0,             // CRC校验码,通常设置为0,除非在原始模式下使用int method = Z_DEFLATED,     // 压缩方法,Z_DEFLATED表示使用Deflate算法,Z_BZIP2ED表示使用BZIP2算法,0表示不压缩int level = Z_DEFAULT_COMPRESSION, // 压缩级别,对于Deflate算法,可以是0-9之间的值,Z_DEFAULT_COMPRESSION表示默认级别// 以下保持默认即可!!!bool raw = false,            int windowBits = -MAX_WBITS, int memLevel = DEF_MEM_LEVEL,int strategy = Z_DEFAULT_STRATEGY 
);

需要注意的是,

src参数,通常设置为0,除非你知道使用其他值,所带来的后果;

method参数,需要设置为8,即宏Z_DEFLATED,这个是压缩算法,注意,quazip只支持这种算法,所以必须设置为8;

level参数,是压缩等级,可设置0-9,之间的任何一个数字,0表示无压缩,最快,9表示最大压缩,但最慢,一般设置为默认值即可!

其实也就是说,只需要设置前面三个参数即可!!!

7).读取写入

使用QFile读取本地文件数据,写入QuaZipFile中

QFile sourceFile(filePath);
sourceFile.open(QIODevice::ReadOnly);
zipFile.write(sourceFile.readAll());    // 注意,处理大文件时,这里会崩溃

注意,上面写法在处理大文件时会有问题,readAll()函数会将文件数据读如内存中,如果文件太大,会将堆内存给撑爆,程序就闪退了;

所以为了避免这样的问题,在处理大文件时,必须分块进行处理!

QByteArray buffer;                                    
while(!sourceFile.atEnd()) {                          buffer = sourceFile.read(4096);    // 每次读取一页内存数据          zipFile.write(buffer);                            
}

8).关闭文件

最后记得在循环尾部将文件关闭后,再进行新一轮的文件读写操作!

sourceFile.close();
zipFile.close();

处理完毕后,记得关闭 newZip.close();

加密压缩文件部分与之类似,这里就不展开讲解了!

给出函数接口,自己动手去实现一下吧;

    /*** @brief 加密压缩多个文件* @param fileCompressed        压缩包名* @param files                 要解压的文件列表* @param password              密码* @return*/bool compressFilesWithPassword(const QString &fileCompressed,const QStringList &files,const QString& password);

9).详细代码

进度部分就不详细讲解了,都在代码函数中

/**                               * @brief 加密压缩文件夹内的所有文件           * @param fileCompressed    压缩包名-路径  * @param dir               压缩路径  * @param password          密码    * @return                        */                               
bool zipCompress::compressDirsWithPassword(const QString &fileCompressed, const QString &dir, const QString &password) {bool result = false;if (fileCompressed.isEmpty() || dir.isEmpty()) {emit signalCompressDirinish(result);return result;}QuaZip newZip(fileCompressed); // 要生成的zip文件if (!newZip.open(QuaZip::mdCreate)) {qDebug() << "Failed to create ZIP file:" << fileCompressed;emit signalCompressDirinish(result);return result;}// 读取目录中的所有文件QStringList filePathList = listFilesInDirectoryRecursively(dir);if (filePathList.isEmpty()) {qDebug() << "No files to compress in directory:" << dir;newZip.close();emit signalCompressDirinish(result);return result;}qreal pro = 100.0 / filePathList.size();qreal progress = 1.0;QuaZipFile zipFile(&newZip);for (const QString &filePath : filePathList) {// 构造相对于dir的文件名QString fileName = QDir(dir).relativeFilePath(filePath);// 创建ZIP文件条目信息QuaZipNewInfo info(fileName, filePath);// 尝试打开ZIP文件条目进行写入if (!zipFile.open(QIODevice::WriteOnly, info, password.toUtf8().constData(), 0, 8)) {qDebug() << "Failed to open ZIP entry for writing:" << fileName;// 关闭ZIPnewZip.close();emit signalCompressDirinish(result);return result;}// 打开源文件进行读取QFile sourceFile(filePath);if (!sourceFile.open(QIODevice::ReadOnly)) {qDebug() << "Failed to open source file for reading:" << filePath;// 关闭ZIPnewZip.close();emit signalCompressDirinish(result);return result;}// 总进度emit signalCompressionProgress(progress, "1", filePath);// 大文件读取分块处理,否则程序会闪退{qreal file_pro = 100.0 / (sourceFile.size() / mBlockSize);qreal file_progress = 0.0;// 将源文件的内容写入ZIP文件QByteArray buffer;while(!sourceFile.atEnd()) {buffer = sourceFile.read(mBlockSize);zipFile.write(buffer);file_progress += file_pro;emit signalFileProgress(file_progress, filePath);}emit signalFileProgress(100, filePath);}sourceFile.close();zipFile.close();progress += pro;progress = qBound(0.0, progress, 99.999);}result = true;newZip.close();emit signalCompressionProgress(100, "1", "");emit signalCompressDirinish(result);return result;
}QStringList zipCompress::listFilesInDirectoryRecursively(const QString &directoryPath) {QStringList list;QDirIterator it(directoryPath, QDir::Files, QDirIterator::Subdirectories);while (it.hasNext()) {QString filePath = it.next();list.append(filePath);}return list;
}

2.解密解压缩

这里处理的是解压缩一个zip文件;

解压出来文件结构与压缩包内一致!

1).创建文件夹

判断传进来的路径解压路径,如果不存在则创建

 2).打开zip文件

QuaZip zip(fileCompressed);                                       
if (!zip.open(QuaZip::mdUnzip)) {                                 qDebug() << "Failed to open ZIP file:" << fileCompressed;                          return false;                                                
}

因为是解压缩,所以参数使用mdUnzip

3).获取zip内的所有文件条目

QList<QuaZipFileInfo> fileInfoList = zip.getFileInfoList();

4).遍历这个链表

解压缩操作都在这个循环体内进行;

5).处理文件夹

如果处理的当前文件,在本机电脑中还没有路径,则进行创建;

且跳过是文件夹的操作!

 6).创建一个QuaZipFile 

QuaZipFile zipFile(zip.getZipName(), 文件名);

7).打开QuaZipFile

zipFile.open(QIODevice::ReadOnly, password.toUtf8().constData());

参数二是密码;

8).读取写入(解压缩)

注意,为了防止大文件的问题,读取写入是还是分块进行操作! 

QByteArray buffer;                                        
while(!zipFile.atEnd()) {                                 buffer = zipFile.read(4096);                    outFile.write(buffer);                                  
}

 9).关闭文件

最后记得在循环尾部将文件关闭后,再进行新一轮的文件读写操作!

outFile.close();    
zipFile.close();    
处理完毕后,记得关闭 zip.close();

10).详细代码

进度部分就不详细讲解了,都在代码函数中

bool zipCompress::extractDirWithPassword(const QString &fileCompressed, const QString &dir, const QString &password) {bool result = false;if (fileCompressed.isEmpty() || dir.isEmpty()) {emit signalExtractDirFinish(result);return result;}// 使用QFileInfo获取文件名(不带扩展名)并构建目标目录路径QFileInfo fileInfo(fileCompressed);QString targetDirName = fileInfo.completeBaseName();QString targetDirPath = QDir(dir).absoluteFilePath(targetDirName);// 创建目标目录(如果不存在)QDir targetDir(targetDirPath);if (!targetDir.exists() && !targetDir.mkpath(".")) {qDebug() << "Failed to create target directory:" << targetDirPath;emit signalExtractDirFinish(result);return result;}// 打开ZIP文件QuaZip zip(fileCompressed);if (!zip.open(QuaZip::mdUnzip)) {qDebug() << "Failed to open ZIP file:" << fileCompressed;emit signalExtractDirFinish(result);return result;}// 遍历ZIP文件中的所有条目QList<QuaZipFileInfo> fileInfoList = zip.getFileInfoList();qreal pro = 100.0 / fileInfoList.size();qreal progress = 1.0;for (const QuaZipFileInfo &fileInfoEntry : fileInfoList) {// 组合目标文件路径QString fileName = fileInfoEntry.name;QString filePath = targetDir.absoluteFilePath(fileName);// 如果是文件夹,如果不存在则创建;然后跳过文件夹的操作QFileInfo info(filePath);if (info.isDir()) {QDir().mkpath(filePath);continue;} else {    // 如果是文件,如果路径不存在,创建该路径QString path = info.path();QDir dpath(path);if (!dpath.exists()) {if (!dpath.mkpath(path)) {qDebug() << "Failed to create directory:" << path;emit signalExtractDirFinish(result);return result;}}}// 打开ZIP条目并解压文件QuaZipFile zipFile(zip.getZipName(), fileName);if (zipFile.open(QIODevice::ReadOnly, password.toUtf8().constData())) {QFile outFile(filePath);if (!outFile.open(QIODevice::WriteOnly)) {qDebug() << "Failed to open output file for writing:" << filePath;zipFile.close();zip.close();emit signalExtractDirFinish(result);return result;}emit signalCompressionProgress(progress, "2", filePath);// 大文件读取分块处理,否则程序会闪退{qreal file_pro = 100.0 / (zipFile.size() / mBlockSize);qreal file_progress = 0.0;// 解压并写入文件QByteArray buffer;while(!zipFile.atEnd()) {buffer = zipFile.read(mBlockSize);outFile.write(buffer);file_progress += file_pro;emit signalFileProgress(file_progress, filePath);}emit signalFileProgress(100, filePath);}outFile.close();zipFile.close();} else {qDebug() << "Failed to open ZIP entry for reading:" << fileName;zip.close();emit signalExtractDirFinish(result);return result;}progress += pro;progress = qBound(0.0, progress, 99.999);}result = true;// 关闭ZIP文件zip.close();emit signalCompressionProgress(100, "2", "");emit signalExtractDirFinish(result);return result;
}

三、源码分享

到此,解压缩已经介绍完毕,现在分享我参考写的解压缩程序

https://download.csdn.net/download/cpp_learner/90411497https://download.csdn.net/download/cpp_learner/90411497因为我这边编写的压缩和解压缩操作都是同步的,所以,建议操作时使用线程去处理,否则会卡屏,而且进度条也无法使用;

在我给出的案例工程中,就是使用了线程去解压缩等操作,有兴趣可以学习一下!

1.下载后如何引入自己的项目工程中?

1).拷贝quazip文件夹到自己的项目工程中

2).在自己的项目的.pro文件引入如下代码

# 源码方式使用需要设置为静态库
DEFINES +=   QUAZIP_STATIC
include($$PWD/quazip/3rdparty/zlib.pri)
include($$PWD/quazip/quazip.pri)
include($$PWD/quazip/zipop/zipop.pri)

3).包含头文件

#include "zipcompress.h"

头文件包含后,就可以直接使用了 

compressionTool::zipCompress m_zipCompress;                                  
// 压缩文件夹                                                                     
m_zipCompress.compressDirsWithPassword(mZipPath, mPath, mPassword);        // 解压缩                                                                       
m_zipCompress.extractDirWithPassword(fileName, fileinfo.path(), "abc@123");  // 压缩文件                                                                    
m_zipCompress.compressFilesWithPassword(mZipPath, mFiles, mPassword);      

2.QT自带压缩库操作分享 ZipReader、QZipWriter

以下是我编写好的操作类,直接新建文件拷贝代码到文件中即可使用!

(注意,一般来说,需要QT5.14以上的版本才可以使用)

1).导入私有模块

.pro文件中添加私有模块

QT       += gui-private

2).zipcompress.h

#ifndef ZIPCOMPRESS_H
#define ZIPCOMPRESS_H#include <QObject>
#include <QtGui/private/qzipreader_p.h>
#include <QtGui/private/qzipwriter_p.h>class ZipCompress : public QObject
{Q_OBJECT
public:ZipCompress(QObject *parent = nullptr);/*** @brief 压缩文件* @param fileNames 需要压缩的文件名* @param saveName  压缩后的文件路径和名字*/bool zipCompressFile(const QStringList &fileNames, const QString &saveName);/*** @brief 压缩文件夹* @param dirName   需要压缩的文件夹名* @param saveName  压缩后的文件路径和名字*/bool zipCompressDirector(const QString &dirName, const QString &saveName);/*** @brief 解压缩* @param zipFile   压缩包* @param saveDir   解压路径* @return*/bool zipUnCompressFile(const QString &zipFile, const QString &saveDir);private:/*** @brief 递归压缩* @param zipWriterHandle       QZipWriter指针* @param zipParentDirName      父路径,相对路径* @param srcDir                当前需要处理的文件夹路径,绝对路径*/void zipCompressDirector(void *zipWriterHandle, const QString &zipParentDirName, const QString &srcDir);private:QZipReader *mZipReader;QZipWriter *mZipWriter;
};#endif // ZIPCOMPRESS_H

3).zipcompress.cpp

#include "zipcompress.h"#include <QFileInfo>
#include <QDir>
#include <QDebug>ZipCompress::ZipCompress(QObject *parent): QObject(parent)
{
}bool ZipCompress::zipCompressFile(const QStringList &fileNames, const QString &saveName)
{QZipWriter *zipWriter = new QZipWriter(saveName);if (!zipWriter) {qDebug() << "压缩失败!";return false;}QFile file;QFileInfo fileInfo;foreach (const QString &fileName, fileNames) {if (fileName.isEmpty() || "" == fileName) {continue;}// 判断文件是否存在fileInfo.setFile(fileName);if (!fileInfo.exists()) {qDebug() << fileName << " : 文件不存在!";continue;}if (fileInfo.isFile()) {// 打开文件file.setFileName(fileName);if (!file.open(QIODevice::ReadOnly)) {qDebug() << fileName << " : 文件打开失败!";continue;}// 添加到压缩包中zipWriter->addFile(fileInfo.fileName(), file.readAll());file.close();} else if (fileInfo.isDir()) {// 添加到压缩包中zipWriter->addDirectory(fileName);}}zipWriter->close();if (zipWriter) {delete zipWriter;zipWriter = nullptr;}return true;
}bool ZipCompress::zipCompressDirector(const QString &dirName, const QString &saveName)
{QFile file(saveName);if (!file.open(QFile::WriteOnly)) {qDebug() << "压缩失败!";return false;}std::shared_ptr<QZipWriter> zipWriter(new QZipWriter(&file));if (!zipWriter) {qDebug() << "压缩失败!";return false;}zipCompressDirector(zipWriter.get(), "", dirName);zipWriter->close();file.close();return true;
}bool ZipCompress::zipUnCompressFile(const QString &zipFile, const QString &saveDir)
{QFile file(zipFile);if (!file.open(QIODevice::ReadOnly)) {qDebug() << zipFile << " : 文件打开失败!";return false;}QDir dir(saveDir);if (!dir.exists()) {if (!dir.mkpath(saveDir)) { // 创建文件夹qDebug() << saveDir << " : 文件夹路径不存在,且创建失败!";return false;}}std::shared_ptr<QZipReader> zipReader(new QZipReader(&file));return zipReader->extractAll(saveDir);  // 全部解压出来
}void ZipCompress::zipCompressDirector(void *zipWriterHandle, const QString &zipParentDirName, const QString &srcDir)
{QZipWriter *zipWriter = (QZipWriter *)zipWriterHandle;QDir directory(srcDir);QFileInfoList fileList = directory.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot);for (const QFileInfo &fileInfo : fileList) {QString relatePath = fileInfo.fileName();       // 获取当前文件名 | 文件夹名if (!zipParentDirName.isEmpty()) {relatePath.prepend(zipParentDirName + "/"); // 头部拼接上父路径,相对路径}QString filePath = fileInfo.absoluteFilePath(); // 获取当前文件的绝对路径if (fileInfo.isDir()) {// 添加文件夹zipWriter->addDirectory(relatePath);// 参数二:文件夹相对路径      参数三:文件夹绝对路径zipCompressDirector(zipWriter, relatePath, filePath);} else {QFile file(filePath);if (!file.open(QIODevice::ReadOnly)) {qDebug() << filePath << " : 文件打开失败!";continue;}zipWriter->addFile(relatePath, file.readAll());file.close();}}
}

四、结束语

文章最后,感谢各位能够坚持看完,相信此篇文章也会对你有所帮助;

当然,我上面所给出的只是一些基础用法,还有很多高级用法我也还没整明白;

或者哪位大佬有更好的实现方式,也欢迎评论区分享出来,供大家一起探讨学习;

完!

相关文章:

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

前言 最近在做项目时遇到一个需求&#xff0c;需要将升级的文件压缩成zip&#xff0c;再进行传输&#xff1b; 通过网络调研&#xff0c;有许多方式可以实现&#xff0c;例如QT私有模块的ZipReader、QZipWriter&#xff1b;或者第三方库zlib或者libzip或者quazip等&#xff1…...

深入解析桥接模式:软件设计中的解耦利器

桥接模式&#xff1a;软件设计中的解耦利器 在软件开发的复杂世界中&#xff0c;设计模式是开发者解决常见问题的有力工具。桥接模式作为一种重要的结构型设计模式&#xff0c;在处理抽象与实现的关系时展现出独特的优势&#xff0c;它能够巧妙地将抽象部分与实现部分分离&…...

MYSQL-数据库-DDL-DML-DQL-DCL-基础学习

MySql概念&#xff1a; 建立在关系模型基础上&#xff0c;有多张相互连接的二维表组成的数据库 SQL通用语法&#xff1a; 1.SQL语句可以单行或多行书写&#xff0c;以分号结尾 2.SQL语句可以使用空格/缩进来增强语句的可读性 3.MySQL数据库的SQL语句不区分大小写&#xff0c;关…...

rv1126解码的一些原理

rv1126解码篇中&#xff0c;出现最重要的两个api一个是&#xff0c;send_vdec_thread线程里面调用的RK_MPI_SYS_SendMediaBuffer&#xff0c;把数据发到解码器。另外一个是read_vdec_thread线程的RK_MPI_SYS_GetMediaBuffer获取解码器里面的数据。 今天想探讨一下他的底层原理。…...

二级公共基础之数据结构与算法篇(七)排序技术

目录 前言 一、交换类排序 1.冒泡排序法 1. 冒泡排序的思想 2. 冒泡排序的实现步骤 3. 示例 4. 冒泡排序的特点 2.快速排序 1. 快速排序的核心思想 2. 快速排序的实现步骤 3. 示例代码(C语言) 4. 快速排序的特点 二、插入类排序 1. 简单插入排序 1.简单插入排…...

深蕾科技智能多媒体SoC产品助力“DataEye剧查查之夜”微短剧盛会

深蕾科技助力微短剧盛会 深圳湾“DataEye剧查查之夜”微短剧盛会&#xff0c;于2025年2月20日18:00点&#xff0c;在深圳湾盛大开启。作为第十四届中国国际新媒体短片节的重要组成部分&#xff0c;“剧查查之夜”汇聚了微短剧行业的顶尖力量&#xff0c;吸引了众多大咖齐聚一堂…...

Apache Doris 实现毫秒级查询响应

1. 引言 1.1 数据分析的重要性 随着大数据时代的到来,企业对实时数据分析的需求日益增长。快速、准确地获取数据洞察成为企业在竞争中脱颖而出的关键。传统的数据库系统在处理大规模数据时往往面临性能瓶颈,难以满足实时分析的需求。例如,一个电商公司需要实时监控销售数据…...

计算机考研之数据结构:P 问题和 NP 问题

在算法的时间复杂度估算中&#xff0c;通常教材和题目中的估算结果包括&#xff1a; O ( 1 ) , O ( log ⁡ n ) , O ( n ) , O ( n ) , O ( n log ⁡ n ) , O ( n 2 ) , O ( n 3 ) , O ( log ⁡ log ⁡ n ) O(1),O(\log{n}),O(\sqrt{n}),O(n),O(n\log{n}),O(n^2),O(n^3),O(\log…...

新数据结构(13)——I/O

字符流 字符输入流&#xff08;Reader&#xff09; 字符输入流用于从数据源&#xff08;如文件、字符串等&#xff09;读取字符数据。Reader 是所有字符输入流的抽象基类。 常用实现类 FileReader 用于从文件中读取字符数据。 InputStreamReader 将字节流转换为字符流&…...

PySide6学习专栏(四):用多线程完成复杂计算任务

如果计程序中要处理一个非常庞大的数据集中的数据&#xff0c;且数据处理计算很复杂&#xff0c;造成数据处理占用大量时间和CPU资源&#xff0c;如果不用多线程&#xff0c;仅在主进程中来处理数据&#xff0c;将会使整个程序卡死&#xff0c;必须采用多线程来处理这些数据是唯…...

Python多线程编程理解面试题解析

一、多线程介绍 Python 的多线程是一种实现并发编程的方式&#xff0c;允许程序同时执行多个任务。然而&#xff0c;由于 Python 的全局解释器锁&#xff08;GIL&#xff09;的存在&#xff0c;多线程在某些场景下可能无法充分利用多核 CPU 的性能。以下是对 Python 多线程的理…...

Flutter - 初体验

项目文件目录结构介绍 注&#xff1a;创建 Flutter 项目名称不要包含特殊字符&#xff0c;不要使用驼峰标识 // TODO 开发中运行一个 Flutter 三种启动方式 Run 冷启动从零开始启动Hot Reload 热重载执行 build 方法Hot Restart 热重启重新运行整个 APP 先看效果&#xff0c…...

使用最广泛的Web应用架构

目前互联网中没有一种绝对使用最广泛的Web应用架构&#xff0c;不同的架构在不同的场景和企业中都有广泛应用&#xff0c;但微服务架构和Serverless架构是当前较为主流和广泛使用的架构。以下是对这两种架构的具体分析&#xff1a; 微服务架构 适用场景广泛 大型互联网公司&a…...

YOLOv11-ultralytics-8.3.67部分代码阅读笔记-split_dota.py

split_dota.py ultralytics\data\split_dota.py 目录 split_dota.py 1.所需的库和模块 2.def bbox_iof(polygon1, bbox2, eps1e-6): 3.def load_yolo_dota(data_root, split"train"): 4.def get_windows(im_size, crop_sizes(1024,), gaps(200,), im_rate_t…...

Unity shader glsl着色器特效之 模拟海面海浪效果

一个简单的海浪效果&#xff0c;通过波的叠加实现水面起伏的动效&#xff0c;根据波峰斜率来为浪花着色&#xff0c;再根据法线贴图和水花贴图来和调整uv的平滑移动来增强海浪移动的细节。如果需要更逼真的效果可以考虑在满足浪花触发的地方添加粒子系统 前置效果图 因为是很久…...

`AdminAdminDTO` 和 `userSession` 对象中的字段对应起来的表格

以下是将更正后的表格放在最前面的回答&#xff0c;表格包含序号列&#xff0c;合并了后端 AdminAdminDTO 和前端 userSession 的所有字段&#xff0c;并标注对方没有的字段。token 字段值用省略号&#xff08;...&#xff09;表示&#xff1a; 序号字段名AdminAdminDTO (后端…...

sqlserver查询内存使用情况的方法

查询 这个SQL查询用于获取当前数据库实例中各个数据库在缓冲池&#xff08;buffer pool&#xff09;中的数据页所占用的内存大小。 select isnull(db_name(database_id),ResourceDb) AS DatabaseName,CAST(COUNT(row_count) * 8.0 /(1024.0) AS DECIMAL(28,2)) AS [size (MB…...

rust笔记7-生命周期显式标注

Rust 的生命周期(Lifetimes)是 Rust 内存安全模型的核心部分,用于确保引用始终有效,避免悬垂引用(Dangling References)。下面我们从生命周期的设计出发点、标注语法以及在不同上下文中的应用(函数、方法、结构体、trait 等)来详细介绍。 1. 生命周期设计的出发点 Rus…...

SQL Server 导入Excel数据

1、选中指定要导入到哪个数据库&#xff0c;右键选择 》任务 》导入数据 2、数据源 选择Excel&#xff0c;点击 下一步(Next) 3、目前 选择OLE DB Provider &#xff0c;点击 下一步&#xff08;Next&#xff09; 4、默认 &#xff0c;点击 下一步&#xff08;Next&#xff09;…...

【笔记】LLM|Ubuntu22服务器极简本地部署DeepSeek+联网使用方式

2025/02/18说明&#xff1a;2月18日~2月20日是2024年度博客之星投票时间&#xff0c;走过路过可以帮忙点点投票吗&#xff1f;我想要前一百的实体证书&#xff0c;经过我严密的计算只要再拿到60票就稳了。一人可能会有多票&#xff0c;Thanks♪(&#xff65;ω&#xff65;)&am…...

AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题

分区配置 (ptab.json) img 属性介绍&#xff1a; img 属性指定分区存放的 image 名称&#xff0c;指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件&#xff0c;则以 proj_name:binary_name 格式指定文件名&#xff0c; proj_name 为工程 名&…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...

k8s从入门到放弃之HPA控制器

k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率&#xff08;或其他自定义指标&#xff09;来调整这些对象的规模&#xff0c;从而帮助应用程序在负…...