当前位置: 首页 > 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…...

Ubuntu 安装 Nginx并配置反向代理

Ubuntu版本&#xff1a;Ubuntu 24.04.2 LTS 一、安装Nginx ​更新系统软件包​ 安装前需确保系统处于最新状态&#xff0c;避免依赖冲突 sudo apt update && sudo apt upgrade -y ​安装Nginx主程序​ Ubuntu官方仓库已包含稳定版Nginx&#xff0c;直接安装即可 sudo…...

GitHub SSH连接问题解决指南

&#x1f50d; GitHub SSH连接问题解决指南 问题描述 遇到错误&#xff1a;ssh: connect to host github.com port 22: Connection refused 说明您的网络环境无法访问GitHub的SSH端口22&#xff0c;常见原因&#xff1a; 防火墙/网络运营商限制&#xff08;国内常见&#xf…...

C++ 跨平台的 GetCurrentThreadId() 获取当前线程ID实现

支持&#xff1a;C11 及早前标准库版本&#xff0c;而无需使用&#xff1a;std::this_thread::get_id()。 支持&#xff1a;NDK/ANDROID、Windows、Linux、MacOS X 等多个操作系统平台。 int64_t GetCurrentThreadId() noexcept { #if defined(_WIN32) || defined(_WIN64)retu…...

钉钉MAKE AI生态大会思考

1. 核心特性 1.1 底层模型开放 除原有模型通义千问外,新接入猎户星空、智普、MinMax、月之暗面、百川智能、零一万物。 1.2 AI搜索 AI搜索贯通企业和个人散落在各地的知识(聊天记录、文档、会议、日程、知识库、项目等),通过大模型对知识逻辑化,直接生成搜索的答案,并…...

SQL笔记#复杂查询

一、视图 1、视图和表 使用试图时会执行SELECT语句并创建一张临时表。视图中保存的是SELECT语句;表中保存的是实际数据。 2、创建视图的方法 CREATE VIEW 视图名称(<视图列名1>,<视图列名2>,……) AS <SELECT语句> CREATE VIEW ProductSum (prod…...

【Linux】基于UDP/TCP套接字编程与守护进程

目录 一、网路套接字编程 &#xff08;一&#xff09;基础概念 1、源IP地址与目的IP地址 2、端口号 3、TCP与UDP 4、网络字节序 &#xff08;二&#xff09;套接字编程接口 1、socket 常见API 2、sockaddr结构 &#xff08;三&#xff09;UDP套接字 1、UDP服务器创建…...

springboot 引入前端

前端 打包 npm run build vue.config.js 文件 publicPath 默认建议保持 / publicPath: ‘/’ 后端 目录 粘贴下面目录之一&#xff1a; src/main/resources/static/ src/main/resources/public/ 补充&#xff08;用的少&#xff09; server:servlet:context-path: /thirdAdm…...

RTSP/Onvif安防平台EasyNVR接入EasyNVS显示服务缺失的原因与解决方案

EasyNVS云管理平台具备强大的汇聚与管理功能&#xff0c;支持EasyGBS、EasyNVR等平台的接入&#xff0c;能够将接入的视频资源进行统一输出&#xff0c;提供远程可视化运维等管理功能&#xff0c;特别适合解决设备现场没有固定公网IP但仍需在公网直播的需求。 在某次用户现场部…...

算法系列之回溯算法

在计算机科学领域&#xff0c;算法是解决问题的核心。回溯算法作为一种经典的算法设计技巧&#xff0c;以其试错和回退的思想&#xff0c;在解决许多复杂问题时展现出强大的能力。本文将深入探讨回溯算法&#xff0c;包括其核心概念、实现步骤、代码示例以及适用场景&#xff0…...

Uniapp 小程序接口封装与使用

深入理解 Uniapp 小程序接口封装与使用 在 Uniapp 小程序开发中&#xff0c;接口请求是获取和交互数据的关键部分。合理地封装接口不仅能提高代码的可维护性&#xff0c;还能增强项目的健壮性。今天&#xff0c;我们就来详细探讨一下如何在 Uniapp 中进行接口封装、引入以及使…...

Harmony开发笔记(未完成)

一、感想 作为一名拥有11年经验的Android开发者&#xff0c;我亲历了Android从高速发展到如今面临“僧多粥少”的过程。技术的世界瞬息万变&#xff0c;没有一种技术能够让人依赖一辈子。去年初&#xff0c;我自学了鸿蒙系统&#xff0c;并顺利通过了鸿蒙官方的初级和高级认。…...

观成科技:海莲花“PerfSpyRAT”木马加密通信分析

1.概述 在2024年9月中旬至10月&#xff0c;东南亚APT组织“海莲花”通过GitHub发布开源安全工具项目&#xff0c;针对网络安全人员发起了定向攻击。通过对相关攻击活动进行分析&#xff0c;可以将其与一些海莲花的样本关联起来。这些样本的通信数据结构与海莲花此前使用的攻击…...

Spring Boot @Async 注解深度指南

Spring Boot Async 注解深度指南 一、核心使用要点 启用异步支持 必须在启动类或配置类添加 EnableAsync&#xff0c;否则异步不生效。 SpringBootApplication EnableAsync public class Application { ... }线程池配置 默认问题&#xff1a;Spring 默认使用 SimpleAsyncTaskEx…...

windows设置暂停更新时长

windows设置暂停更新时长 win11与win10修改注册表操作一致 &#xff0c;系统界面不同 1.打开注册表 2.在以下路径 \HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsUpdate\UX\Settings 右键新建 DWORD 32位值&#xff0c;名称为FlightSettingsMaxPauseDays 根据需求填写数…...

Orange 开源项目 - 集成百度智能云-千帆大模型

1 集成百度智能云-千帆大模型 百度智能云-千帆ModelBuilder百度智能云千帆大模型服务与开发平台ModelBuilder&#xff08;以下简称千帆ModelBuilder&#xff09;是面向企业开发者的一站式大模型开发及服务运行平台。千帆ModelBuilder不仅提供了包括文心一言底层模型和第三方开源…...

特斯拉 FSD 算法深度剖析:软件层面全解读

一、引言 特斯拉的 FSD&#xff08;Full Self-Driving&#xff09;系统作为自动驾驶领域的前沿成果&#xff0c;其软件层面的算法设计至关重要。本文将从软件的角度&#xff0c;深入探讨特斯拉 FSD 所采用的算法&#xff0c;包括感知、规划、控制等多个方面&#xff0c;以期为…...

2025/2/17--2/23学习笔记(week1)_C语言

1 整数的存储 只有整数才有原码&#xff0c;反码&#xff0c;补码&#xff0c;原码取反加一&#xff08;除了符号位&#xff09;得到补码。补码的补码会变成原码。 在任何位运算里&#xff0c;都是操作的补码&#xff0c;因为整数在内存里都是以补码存储的 2 移位运算符 移位…...

数据结构:二叉树的数组结构以及堆的实现详解

目录 一.树与二叉树 1.树的概念与相关术语&#xff1a; 2.二叉树&#xff1a; &#xff08;1&#xff09;定义&#xff1a; &#xff08;2&#xff09;特殊的二叉树&#xff1a; &#xff08;3&#xff09;完全二叉树 &#xff08;4&#xff09;二叉树的存储结构&#x…...

AWS S3 如何设置公开访问权限?

1.让整个bucket都有公开访问权限 1.1关闭【阻止公共读】 1.2关闭ACL访问控制 1.3打开桶策略 这样桶内所有的图片就能访问了 2.只开放特定文件让其具有访问权限&#xff1f; 2.1关闭【阻止公共读】 如之前的图示 2.2打开ACL控制 2.3单个文件打开公共读...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)

升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点&#xff0c;但无自动故障转移能力&#xff0c;Master宕机后需人工切换&#xff0c;期间消息可能无法读取。Slave仅存储数据&#xff0c;无法主动升级为Master响应请求&#xff…...

[Java恶补day16] 238.除自身以外数组的乘积

给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O(n) 时间复杂度…...

08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险

C#入门系列【类的基本概念】&#xff1a;开启编程世界的奇妙冒险 嘿&#xff0c;各位编程小白探险家&#xff01;欢迎来到 C# 的奇幻大陆&#xff01;今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类&#xff01;别害怕&#xff0c;跟着我&#xff0c;保准让你轻松搞…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...

Mysql故障排插与环境优化

前置知识点 最上层是一些客户端和连接服务&#xff0c;包含本 sock 通信和大多数jiyukehuduan/服务端工具实现的TCP/IP通信。主要完成一些简介处理、授权认证、及相关的安全方案等。在该层上引入了线程池的概念&#xff0c;为通过安全认证接入的客户端提供线程。同样在该层上可…...