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

SQLMesh 系列教程8- 详解 seed 模型

在数据分析和建模过程中&#xff0c;外部模型&#xff08;External Models&#xff09;在 SQLMesh 中扮演着重要角色。外部模型允许用户引用外部数据源或现有数据库表&#xff0c;从而实现灵活的数据整合和分析。本文将介绍外部模型的定义、生成方法&#xff08;包括使用 CLI 和…...

oracle apex post接口

日常记录 使用到了apex_json方式接收 、、、1 首先&#xff0c;接口通过body传递过来&#xff0c;成功接收到&#xff0c; 数据格式为 JSON_OBJECT_T l_json : JSON_OBJECT_T.parse(:body); 这里我用参数接收到 然后 里面是包含了 "data" 我用 继续接收到这个 l…...

复制所绑定元素文本的vue自定义指令

最近写了一个复制所绑定元素文本的vue自定义指令&#xff0c;给大家分享一下。 import { ElMessage } from element-plus// data-* 属性名 const dataCopyBtnTextAttribute data-copy-btn-text // 复制按钮的class&#xff0c;结合项目实际进行设置 const copyBtnClass icon…...

若依-@Excel新增注解numberFormat

Excel注解中原本的scale会四舍五入小数&#xff0c;导致进度丢失 想要的效果 显示的时候保留两个小数真正的数值是保留之前的数值 还原过程 若以中有一個專門的工具类&#xff0c;用来处理excel的 找到EXCEL导出方法exportExcel()找到writeSheet,写表格的方法找到填充数据的方法…...

内容中台重构智能服务:人工智能技术驱动精准决策

内容概要 现代企业数字化转型进程中&#xff0c;内容中台与人工智能技术的深度融合正在重构智能服务的基础架构。通过整合自然语言处理、知识图谱构建与深度学习算法三大技术模块&#xff0c;该架构实现了从数据采集到决策输出的全链路智能化。在数据层&#xff0c;系统可对接…...

网络安全:DeepSeek已经在自动的挖掘漏洞

大家好,我是AI拉呱,一个专注于人工智领域与网络安全方面的博主,现任资深算法研究员一职,兼职硕士研究生导师;热爱机器学习和深度学习算法应用,深耕大语言模型微调、量化、私域部署。曾获多次获得AI竞赛大奖,拥有多项发明专利和学术论文。对于AI算法有自己独特见解和经验…...

C#从入门到精通(34)—如何防止winform程序被同时打开多次

前言&#xff1a; 大家好&#xff0c;我是上位机马工&#xff0c;硕士毕业4年年入40万&#xff0c;目前在一家自动化公司担任软件经理&#xff0c;从事C#上位机软件开发8年以上&#xff01;我们在开发上位机软件的过程中&#xff0c;评判一个人软件写的好不好&#xff0c;有一…...

Linux系统使用Docker部署Geoserver并做数据挂载进行地图服务的发布和游览

文章目录 1、前提环境2、拉取geoserver镜像3、创建数据挂载目录4、 运行容器5、 测试使用&#xff08;发布shp数据为服务&#xff09;5.1、创建工作区5.2、添加数据存储5.3、发布图层5.4、服务游览 1、前提环境 部署环境&#xff1a;Linux&#xff0c;Centos7 &#xff0c;Doc…...

STM32-温湿度上传OneNET项目

一、项目需求 使用 ESP8266 连接 OneNET 云平台&#xff0c;并通过 MQTT 协议上传 DHT11 获取的温湿度值。 二、项目框图 三、DHT11工作原理 参考于良许嵌入式手把手教你玩转DHT11&#xff08;原理驱动&#xff09; | 良许嵌入式 3.1 正常工作验证 #​ 上电后&#xff…...

HTML 中的 Canvas 样式设置全解

在 HTML5 中&#xff0c;<canvas> 元素提供了一个强大的绘图接口&#xff0c;允许开发者通过 JavaScript 实现各种图形和动画效果。为了充分利用 <canvas> 的功能&#xff0c;理解其样式设置是至关重要的。本文将详细介绍如何在 HTML 中设置 <canvas> 的各种…...

【Java】File 类

目录 File 类File 类构造方法常见成员方法判断与获取创建与删除获取并遍历 File 类 File 对象表示一个路径&#xff0c;可以是文件的路径&#xff0c;也可以是文件夹的路径 这个路径可以是存在的&#xff0c;也允许是不存在的 绝对路径和相对路径的区别&#xff1a; 绝对路径…...

SAP S4HANA Administration (Mark Mergaerts Bert Vanstechelman)

SAP S4HANA Administration (Mark Mergaerts Bert Vanstechelman)...

特征提取:如何从不同模态中获取有效信息?

在多模态学习中,特征提取是一个至关重要的过程。它是将原始数据(如文本、图像、视频和语音等)转化为机器能够理解和处理的特征的核心步骤。不同于传统的单一模态任务,在多模态学习中,如何有效地从每种模态中提取出有意义的信息并进行融合,直接影响到最终模型的性能和准确…...

2025年-G14-Lc88-278.第一个坏版本 -java版

1.题目描述 第一个坏版本 你是一名产品经理&#xff0c;目前领导一个团队开发新产品。不幸的是&#xff0c;你产品的最新版本未通过质量检查。由于每个版本都是基于前一个版本开发的&#xff0c;所以坏版本之后的所有版本也都是坏的。假设你有 n 个版本 [1, 2, …, n]&#xff…...

计算机网络————(一)HTTP讲解

基础内容分类 从TCP/IP协议栈为依托&#xff0c;由上至下、从应用层到基础设施介绍协议。 1.应用层&#xff1a; HTTP/1.1 Websocket HTTP/2.0 2.应用层的安全基础设施 LTS/SSL 3.传输层 TCP 4.网络层及数据链路层 IP层和以太网 HTTP协议 网络页面形成基本 流程&#xff1a…...

大语言模型架构:从基础到进阶,如何理解和演变

引言 你可能听说过像 ChatGPT 这样的 AI 模型&#xff0c;它们能够理解并生成自然语言文本。这些模型的背后有着复杂的架构和技术&#xff0c;但如果你了解这些架构&#xff0c;就能明白它们是如何工作的。今天&#xff0c;我们将用简单的语言&#xff0c;逐步介绍大语言模型的…...

科普mfc100.dll丢失怎么办?有没有简单的方法修复mfc100.dll文件

当电脑频繁弹窗提示“mfc100.dll丢失”或应用程序突然闪退时&#xff0c;这个看似普通的系统文件已成为影响用户体验的核心痛点。作为微软基础类库&#xff08;MFC&#xff09;的核心组件&#xff0c;mfc100.dll直接关联着Visual Studio 2010开发的大量软件运行命脉。从工业设计…...

什么是虚拟内存?它的作用是什么?

虚拟内存概念 虚拟内存是计算机系统内存管理的一种技术。它使得应用程序认为它拥有连续的可用的内存&#xff08;一个连续完整的地址空间&#xff09;。但是实际上&#xff0c;它通常是被分隔成多个物理内存碎片&#xff0c;还有部分暂时存储在外部磁盘存储器上&#xff0c;在…...

SAP任命Simon Davies为亚太区总裁,领导重组后的亚太地区业务

2025年2月19日&#xff0c;SAP宣布任命Simon Davies为新任亚太区总裁&#xff0c;负责领导公司重组后的亚太地区业务。Davies将常驻新加坡&#xff0c;全面负责SAP在亚太地区的战略、运营、人员管理、销售、服务、合作伙伴关系及盈利能力。他的职责范围涵盖韩国、澳大利亚、新西…...

Markdown使用方法文字版解读

[TOC](这里写自定义目录标题) # Markdown编辑器 你好&#xff01; 这是你第一次使用 **Markdown编辑器** 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章&#xff0c;了解一下Markdown的基本语法知识。 ## 新的改变 我们对Markdown编辑器进行了…...

QT移植,交叉编译至泰山派RK3566开发板,.pro文件解析

备注 交叉编译到开发板,会有各种奇奇怪怪的问题, 直接命令行安装 QTCREATOR和 QtBase,就在板子上搞个桌面系统编译,最后把桌面关掉。 配置文件解析 配置文件丢这里,后面有空整理下。 说下大概的注意点, 安装路径(qtcreator远程部署的路径)、 动态库路径和…...

Liunx(CentOS-6-x86_64)系统安装MySql(5.6.50)

一&#xff1a;安装Liunx&#xff08;CentOS-6-x86_64&#xff09; 安装Liunx&#xff08;CentOS-6-x86_64&#xff09; 二&#xff1a;下载MySql&#xff08;5.6.50&#xff09; MySql下载官网 二&#xff1a;安装MySql 2.1 将mysql上传到Liunx 文件地址 /usr/local/ 2…...

使用 deepseek实现 go语言,读取文本文件的功能,要求支持 ascii,utf-8 等多种格式自适应

使用 deepseek实现 go语言&#xff0c;读取文本文件的功能&#xff0c;要求支持 ascii&#xff0c;utf-8 等多种格式自适应我要用 chatgpt&#xff0c;也问过&#xff0c;但是比 deepseek 还是差一个级别&#xff0c;具体如下&#xff1a; package mainimport ("bufio&qu…...

当电脑上有几个python版本Vscode选择特定版本python

查看当前vscode用的python版本命令 Import sys print(sys.version) 修改VSCODE解释器 打开 VSCode。 按下 CtrlShiftP打开命令面板。 输入 Python: Select Interpreter 并选择它。 从弹出的列表中选择你安装的 Python 解释器。如果你有多个 Python 版本&#xff08;例如…...

大一计算机的自学总结:一维差分与等差数列差分

前言 差分和前缀和一样&#xff0c;也是很重要的基础算法。 一、一维差分 1.内容 当给出一个数组&#xff0c;每次操作让数组某个区间上的值全增加&#xff0c;最后要求返回整个数组的结果。若是一次一次去遍历&#xff0c;时间复杂度肯定很难看。差分可以做到在时间复杂度…...

学习dify第一天:整体架构分析

使用marscode AI插件 从分析最火的dify开始学习使用ai提速首先安装插件功能快捷键使用这个工具如何学习项目首先学习dify那就先上官网看文档开始从docker构建脚本学起看下docker-compose.yamldify里边服务的组件现在看api和web模块api项目根目录有Makefile文件,用于构建api和w…...

C语言的内存分配:malloc和free

使用库函数分配和管理内存。在运行时&#xff0c;分配更多的内存给程序使用&#xff0c;主要工具是malloc函数&#xff0c;这个函数接受一个参数&#xff1a;所需要要的内存字节数。malloc函数会找到合适的空闲内存块&#xff0c;这样的内存是匿名的&#xff0c;即malloc分配了…...

为什么 JSON 不能序列化 set

为什么 JSON 不能序列化 set JSON&#xff08;JavaScript Object Notation&#xff09;作为一种广泛使用的数据交换格式&#xff0c;虽然功能强大&#xff0c;但它无法直接序列化 set 类型。本文将从设计原理、实现限制和实际应用角度&#xff0c;探讨这一现象的原因及解决方案…...

XUnity.AutoTranslator-Gemini——调用Google的Gemini API, 实现Unity游戏中日文文本的自动翻译

XunityAutoTranslator-Gemini-API 本项目是一个使用 Flask 框架和 Google Gemini GenAI 模型构建的 Web API 服务&#xff0c;用于将日文unity游戏文本翻译成简体中文。 日文游戏文本AI翻译API (基于Google Gemini) 本项目是一个使用 Flask 框架和 Google Gemini GenAI 模型…...

深刻理解构件生产线

我们可以将构件生产线类比为软件开发中的一种高效、模块化的构建方式。下面&#xff0c;我将结合Java编程的概念来详细讲解构件生产线的含义和实现方式。 一、构件生产线的概念 构件生产线在软件开发中&#xff0c;类似于工厂中的自动化生产线&#xff0c;它通过将复杂的软件…...

nlp 自然语言处理+bert model +问答系统 question answer system(python 完整代码)

pre-trained bert model 预训练好的Bert模型 本地实现问答系统 用这条命令将bert下载到本地: model.save_pretrained("path/to/model") 也有参考这篇文章 https://colab.research.google.com/drive/1uSlWtJdZmLrI3FCNIlUHFxwAJiSu2J0-#scrollTo=AaweLnNXGhTY …...

【Excel】【VBA】根据内容调整打印区域

Excel VBA&#xff1a;自动调整打印区域的实用代码解析 在Excel中&#xff0c;我们经常需要调整打印区域。今天介绍一段VBA代码&#xff0c;它可以根据C列的内容自动调整打印区域。 Dim ws As Worksheet Dim lastRow As Long Dim r As Long 设置当前工作表 Set ws ActiveSh…...

【读书笔记·VLSI电路设计方法解密】问题53:什么是逻辑综合

逻辑综合是将期望的电路行为的抽象形式&#xff08;通常以RTL表示&#xff09;转换为基于逻辑门&#xff08;标准单元&#xff09;的设计实现的过程。这一过程由具有复杂算法的自动综合工具完成。逻辑综合的结果是网表&#xff0c;它由各种标准单元和特殊宏单元组成。该网表的功…...

Redis 的备份机制

Redis 的备份机制 Redis 是一个高性能的基于内存的键值存储数据库&#xff0c;虽然所有操作都在内存中完成&#xff0c;但 Redis 也提供了持久化机制来确保数据的可靠性和可恢复性。这主要通过 RDB 和 AOF 两种备份机制实现。 RDB&#xff08;Redis Database Backup&#xff…...

CountDownlatch实现原理

文章目录 类图及概要核心方法await() 方法await(long timeout, TimeUnit unit) 方法countDown() 方法getCount() 方法 总结 类图及概要 CountDownLatch 内部有个计数器&#xff0c;并且这个计数器是递减的 。 下面就通过源码看看 JDK 开发组在何时初始化计数器&#xff0c;在何…...

如何为自己的 PDF 文件添加密码?在线加密 PDF 文件其实更简单

随着信息泄露和数据安全问题的日益突出&#xff0c;保护敏感信息变得尤为重要。加密 PDF 文件是一种有效的手段&#xff0c;可以确保只有授权用户才能访问或修改文档内容。本文将详细介绍如何使用 CleverPDF 在线工具为你的 PDF 文件添加密码保护&#xff0c;确保其安全性。 为…...

现代未来派品牌海报徽标设计无衬线英文字体安装包 THANKS LAB

THANK LAB 是一种高级未来主义的软字体&#xff0c;将时尚的现代设计与光滑圆润的边缘相结合&#xff0c;营造出大胆而平易近人的美感。这款字体非常适合品牌、海报、标题、UI/UX 和科幻主题项目&#xff0c;旨在激发创造力。THANK LAB Futuristic Soft Font 完全支持拉丁字母、…...

基于Flask框架的食谱数据可视化分析系统的设计与实现

【Flask】基于Flask框架的食谱数据可视化分析系统的设计与实现 &#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 在当今数字化时代&#xff0c;信息可视化已成为一种高效的数据理解和传播手段。…...

Mac arm架构使用 Yarn 全局安装 Vue CLI

dgqdgqdeMacBook-Pro spid-admin % vue --version zsh: command not found: vue要使用 Yarn 安装 Vue CLI&#xff0c;你可以执行以下命令&#xff1a; yarn global add vue/cli这个命令会全局安装 Vue CLI&#xff0c;让你可以使用 vue 命令创建、管理 Vue.js 项目。以下是一…...

矩阵-旋转图像

旋转图像 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。输入&#xff1a;二维数组 输出&#xff1a;void 思路&#xff1a;tempM…...

自用开发的商标和工作小工具!

近日普推知产老杨把个人小站改了版&#xff0c;也把以前业余开发的小工具做了下优化和增加了一些新的小工具&#xff0c;这些在线小工具手机和电脑双适应&#xff0c;普推老杨日常也在用&#xff0c;主要在商标方面和内容制做。 商标名称图样免费生成主要用在可以生成符合商标申…...

基于腾讯云大模型知识引擎×DeepSeek构建八字、六爻赛博算卦娱乐应用

引言 随着DeepSeek的火爆&#xff0c;其强大的思维链让不少人越用越香&#xff0c;由于其缜密的思维和推理能力&#xff0c;不少人开发出了不少花里胡哨的玩法&#xff0c;其中一种就是以八字、六爻为代表的玄学文化正以“赛博玄学”的新形态席卷年轻群体。 针对于八字、六爻…...

八股文实战之JUC:静态方法的锁和普通方法的锁

1、对于staic同步方法锁住的是class类模板&#xff08;Class对象&#xff09; 对象是线程&#xff08;调用者&#xff09; 调用者只有获取资源的锁才能调用 2、普通同步方法 锁住的资源是class对象 对象是线程&#xff08;调用者&#xff09;即&#xff1a; 静态同步方法&a…...

VTK知识学习(42)-基本的图形操作(三)

1、网格平滑 1&#xff09;概述 现代扫描技术的发展使得获取点云数据不再困难&#xff0c;通过曲面重建技术可以获取表面网格来表示各种复杂的实体。但是点云数据中往往存在噪声&#xff0c;这样得到的重建网格通常都需要进行平滑处理。 拉普拉斯平滑是一种常用的网格…...

代码随想录算法训练营day40(补0208)

买卖股票专栏 1.买卖股票最佳时机 贪心法&#xff0c;好想 题目 121. 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的日子 卖…...

python基于深度学习实现遮挡人脸识别系统的详细方案

以下是一个基于深度学习实现遮挡人脸识别系统的详细方案,使用Python语言: 一、需求理解 遮挡人脸识别系统旨在准确识别出即使面部部分被遮挡(如口罩、眼镜等)的人的身份。该系统将利用深度学习技术,结合合适的数据集进行训练,以达到较高的识别准确率。 二、系统架构 …...

【Python爬虫(43)】云端探秘:Python分布式爬虫部署攻略

【Python爬虫】专栏简介&#xff1a;本专栏是 Python 爬虫领域的集大成之作&#xff0c;共 100 章节。从 Python 基础语法、爬虫入门知识讲起&#xff0c;深入探讨反爬虫、多线程、分布式等进阶技术。以大量实例为支撑&#xff0c;覆盖网页、图片、音频等各类数据爬取&#xff…...

Java集合框架大师课:从青铜到王者的数据结构指南(一)

&#x1f680; Java集合框架大师课&#xff1a;从青铜到王者的数据结构指南&#xff08;一&#xff09; &#x1f31f; 系列定位&#xff1a;全网最懂小白的JCF实战教程 | 建议搭配IDE边学边练 &#x1f3af; 学习路线图 第一章&#xff1a;初识JCF江湖 1.1 什么是JCF&#xf…...

Vmware虚拟机Ubantu安装Docker、k8s、kuboard

准备工作: 切换用户&#xff1a;su root关闭防火墙: sudo ufw diasble关闭swap: systemctl stop swap.target systemctl status swap.target systemctl disable swap.target #开机禁用 systemctl stop swap.img.swap systemctl status swap.img.swap关闭虚拟交换分区 vim /…...