C++ 调用7z进行解压缩,并返回解压缩进度和异常信息
原创文章,转载请注明出处。C++ 7z解压缩前言解决方案1>下载bit7z,编译1.1>下载1.2>解压1.3>编译《bit7z-master》,拿到 bit7z64.lib1.4> 准备7z.dll1.2>编码1.2.1>头文件1.2.2>cpp文件1.2.3>使用介绍前言像Word,Excel,PPT等类似的编辑软件都有一个特点,就是可以保
原创文章,转载请注明出处。
C++ 7z解压缩
前言
像Word,Excel,PPT等类似的编辑软件都有一个特点,就是可以保存成一个文件, 方便传阅。比如
我们的PPT里内置了视频图片文字等,它的后缀是pptx。其实这个pptx就是一个压缩文件,你如果把 .pptx 的后缀改成 .rar 的话, 对其解压后你会发现其实这个.pptx就是个压缩文件, 里面存了很多文件。如下图。其实这个就是用到了压缩和解压。 如果你也是做类似的软件,那么使用7z的库还是挺有必要的。
将.pptx改成rar解压后如下图,比如media文件夹就是放你的ppt内视频的。
解决方案
并不是通过7z.exe直接调用命令的方式, 而是通过bit7z+7z.dll进行调用(方便拿到解压缩进度)
点击跳转到7z官网链接地址,可以了解一些7z的信息
1>拿到 bit7z64.lib和7z.dll
bit7z是一个C++的静态库,里面封装了接口,用来调用7-zip的库;
对bit7z进行编译的话,需要下载下面的两个源码:
1.1>下载编译bit7z64.lib需要的源代码
bit7z-3.1.3.zip | bit7z-master.zip 这两个分支我都测试过是可以用的,建议用比较新的。
1.2>解压下载文件
解压,将《lzma1805.7z》解压,将解压后的(下图的文件)拷贝
放到《 bit7z-master.zip》解压后的路径里(bit7z-master\lib\7zSDK\)如下图
1.3>编译《bit7z-master》,拿到 bit7z64.lib
打开 bit7z-master\bit7z.vcxproj 对其编译, 编译选项Win64+Release
如下图,我编译的是Win64下用的。编译成功后在 bit7z-master\bin\x64 目录下会有你编译完的bit7z64.lib。这个就是我们要用的。
1.4> 准备7z.dll
由于bit7z依赖7z的库,所以还要下载7z的dll库才能用。
7z.dll 下载链接:下载链接
打开上面下载链接后,
-
安装下图《.exe 64 位 x64 7-Zip Windows 64 位(Intel 64 或 AMD64)》
-
安装时候让你选安装路径,拷贝记下这个路径
-
安装完毕后,找到安装后的目录,拷贝7-zip.dll出来,和上面编译好的bit7z64.lib放到你的exe路径下,接下来就可以写代码了
1.2>编码
我直接将代码贴出来,下面1.2.3小节有使用的例子代码。
然后再传一个整体的使用到csdn上: 下载链接 ,我发现我每次传文件时候都是选择的下载不需要积分(0分),但是下载的时候还要做任务,这对大家属实有点不友好。
1.2.1>头文件
// Fill out your copyright notice in the Description page of Project Settings.
/*
* Author:田华健 tianhj
* Data: 2021-07-30 09:02:36
* Description:提供调用7z.exe进行解压缩
* 并不是通过7z.exe直接调用命令的方式, 而是通过bit7z+7z.dll进行调用(方便拿到解压缩进度)
解压缩例子: 两种回调监听方式, 代理和std::bind都可以, 二选一.
{
//移除回调
F7ZDelegates::SevenZProgressDelegate.Remove(m_Delegate1);
F7ZDelegates::SevenZExceptionDelegate.Remove(m_Delegate2);
//绑定解压缩的进度回调
m_Delegate1 = F7ZDelegates::SevenZProgressDelegate.AddUObject(this, &USPGameInstance::OnSevenZProgressListen);
//绑定解压缩的异常回调
m_Delegate2 = F7ZDelegates::SevenZExceptionDelegate.AddUObject(this, &USPGameInstance::OnSevenZExceptionListen);
//新建一个7zHelper类
TSharedPtr<FSevenZHelper> p7Z = MakeShared<FSevenZHelper>();
if (p7Z.Get())
{
//解压
{
//设置进度回调
p7Z->SetprogressCallback(std::bind(&USPGameInstance::progressCallback, this, std::placeholders::_1));
//设置异常回调
p7Z->SetBitExceptionCallback(std::bind(&USPGameInstance::BitExceptionCallback, this, std::placeholders::_1));
//执行解压 将TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian.gspp") 用密码password解压到D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian文件夹
p7Z->CompressDecompression(ESPSevenZType::OpType_Decompression, TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian.gspp"), TEXT(""), TEXT("password"));
}
//压缩
{
//设置进度回调
p7Z->SetprogressCallback(std::bind(&USPGameInstance::progressCallback, this, std::placeholders::_1));
//设置异常回调
p7Z->SetBitExceptionCallback(std::bind(&USPGameInstance::BitExceptionCallback, this, std::placeholders::_1));
//执行压缩 将TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian") 用密码password压缩成TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian.gspp")文件
p7Z->CompressDecompression(ESPSevenZType::OpType_Compress, TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian"), TEXT(""), TEXT("password"));
}
}
}
*/
#pragma once
#pragma warning(disable : 4530)
//7Z类型
UENUM()
enum class ESPSevenZType : uint8
{
OpType_None,
OpType_Compress, //压缩
OpType_Decompression, //解压
};
//#ifndef NIMO_OBS_ZLIB_HASAS
//#define NIMO_OBS_ZLIB_HASAS
#include "CoreMinimal.h"
#include <iostream>
#include <string>
#include <functional>
#include "../../bit7z-3.1.3/include/bit7z.hpp"
#include "../../bit7z-3.1.3/include/bit7zlibrary.hpp"
using namespace bit7z;
//7z代理类
class F7ZDelegates
{
public:
/*
* 7z解压缩进度反馈函数
* ESPSevenZType:解压还是压缩?
* FString:传入的文件名
* double:真正的进度
*/
DECLARE_MULTICAST_DELEGATE_ThreeParams(FSevenZProgressDelegate, ESPSevenZType, const FString&, double);
static FSevenZProgressDelegate SevenZProgressDelegate;
/*
* 7z异常抛
* ESPSevenZType:解压还是压缩?
* FString:传入的文件名
* BitException:真正的异常原因
*/
DECLARE_MULTICAST_DELEGATE_ThreeParams(FSevenZExceptionDelegate, ESPSevenZType, const FString&, const bit7z::BitException&);
static FSevenZExceptionDelegate SevenZExceptionDelegate;
};
class FSevenZHelper : public TSharedFromThis<FSevenZHelper>
{
private:
typedef std::function<void(double progress)> ProgressCallback; //参数是进度回调
typedef std::function<void(std::string filename)> MyFileCallback; //其参数是当前正在进行的操作所处理的文件的名称
typedef std::function<void(const bit7z::BitException&)> BitExceptionCallback; //参数是异常项目
ProgressCallback m_pProgressFunc;
MyFileCallback m_pFileFunc;
BitExceptionCallback m_pExpFunc;
public:
FSevenZHelper();
~FSevenZHelper();
/*
* 外部调用这个函数进行解压缩
*
* 参数1: ESPSevenZType 传入是解压还是压缩的枚举
* 参数2: const FString& Src 你要压缩或解压的全路径
* 参数3: 你的最终路径
* 如果该参数传值的时候为空的话
* 1>解压时候会在同级文件夹下创建一个根据Src去掉压缩文件后缀的文件夹
* 2>压缩的话会在同级文件夹下默认压缩成一个.gspp的文件, 其实就是7z格式
* 参数4: 解压或者压缩时候带的密码
*
* Notes:不支持压缩成rar
*/
void CompressDecompression(ESPSevenZType type, const FString& Src, const FString& Dest, const FString& Password);
//外部调用设置进度的回调函数
void SetProgressCB(ProgressCallback upcFunc);
//外部调用设置异常的回调函数
void SetBitExceptionCallback(BitExceptionCallback ExceptionFunc);
//外部调用设置获取当前正在进行操作所处理的文件的名称
void SetFileNameCurOperatorCallback(MyFileCallback ufcFunc);
private:
void Compress(ESPSevenZType type, const FString& Src, const FString& Dest, const FString& Password); //压缩
void Extract(ESPSevenZType type, const FString& Src, const FString& Dest, const FString& Password); //解压
void GetFileSuffix(const FString& Path);//获取文件后缀
void TotalSizeCB(uint64_t total_size); //返回操作的文件总大小的回调函数
void ProgressCB(uint64_t size); //返回进度
void FileNameCB(std::wstring filename); //返回当前正在进行的操作所处理的文件的名称
FString m_SourcePath; //源文件路径
FString m_DestDir; //目标路径
FString m_FileSuffix; //根据原路径拆出来的文件后缀
uint64_t m_FileSize; //源文件大小
FString DefaultSuffix; //默认后缀
ESPSevenZType m_OpType; //当前是解压还是压缩
uint64_t TotalSize; //当前正在处理的文件的总大小
};
//#endif
1.2.2>cpp文件
#include "SevenZHelper.h"
#include "GetRegValue.h"
#include "RegionRiskReadLibrary.h"
F7ZDelegates::FSevenZProgressDelegate F7ZDelegates::SevenZProgressDelegate;
F7ZDelegates::FSevenZExceptionDelegate F7ZDelegates::SevenZExceptionDelegate;
FSevenZHelper::FSevenZHelper()
: m_pProgressFunc(nullptr)
, m_pFileFunc(nullptr)
, m_SourcePath(TEXT(""))
, m_DestDir(TEXT(""))
, m_FileSuffix(TEXT(""))
, m_FileSize(0)
, DefaultSuffix(TEXT("gspp"))
, m_OpType(ESPSevenZType::OpType_None)
{
}
FSevenZHelper::~FSevenZHelper()
{
}
void FSevenZHelper::CompressDecompression(ESPSevenZType type, const FString& Src, const FString& Dest, const FString& Password)
{
m_OpType = type;
if (type == ESPSevenZType::OpType_Compress)
{
Compress(type, Src, Dest, Password);
}
else if (type == ESPSevenZType::OpType_Decompression)
{
Extract(type, Src, Dest, Password);
}
}
void FSevenZHelper::GetFileSuffix(const FString& Path)
{
//默认后缀格式
m_FileSuffix = DefaultSuffix;
FString Temp = FPaths::ConvertRelativePathToFull(Path);
//获取文件后缀
//将其按照路径拆分开
TArray<FString> arrSplit;
Temp.ParseIntoArray(arrSplit, TEXT("/"), true);
if (arrSplit.Num() > 1)
{
Temp.RemoveFromEnd(arrSplit[arrSplit.Num() - 1]);
//拿到最后一个, 再按照.拆分
FString Fin = arrSplit[arrSplit.Num() - 1];
TArray<FString> arrSplitPoint;
Fin.ParseIntoArray(arrSplitPoint, TEXT("."), true);
if (arrSplitPoint.Num() == 2)
{
m_FileSuffix = arrSplitPoint[1];
}
}
}
void FSevenZHelper::TotalSizeCB(uint64_t total_size)
{
TotalSize = total_size;
}
void FSevenZHelper::ProgressCB(uint64_t size)
{
double progress = ((1.0 * size) / TotalSize);
F7ZDelegates::SevenZProgressDelegate.Broadcast(m_OpType, m_SourcePath, progress);
//std::wcout << progress << "%" << std::endl;
if (m_pProgressFunc) {
m_pProgressFunc(progress);
}
}
void FSevenZHelper::FileNameCB(std::wstring filename)
{
std::string temp = wstring2string(filename);
//std::cout << temp.c_str() << std::endl;
if (m_pFileFunc) {
m_pFileFunc(temp);
}
}
//压缩
void FSevenZHelper::Compress(ESPSevenZType type, const FString& Src, const FString& Dest, const FString& Password)
{
if (Src.IsEmpty()) return;
//1>源文件(路径或者单个文件) 你要对其压缩
m_SourcePath = FPaths::ConvertRelativePathToFull(Src);
//2>获取文件后缀
GetFileSuffix(Dest);
if (m_FileSuffix.Compare(TEXT("rar")) == 0)
{
//不支持rar压缩
return;
}
//3>判断当前传入的文件路径是否和解析的后缀一样
if (!Dest.IsEmpty())
{
m_DestDir = FPaths::ConvertRelativePathToFull(Dest);
if (!m_DestDir.EndsWith(m_FileSuffix))
{
m_DestDir.Append(TEXT("."));
m_DestDir.Append(m_FileSuffix);
}
}
else
{
//3>如果目标路径不设置的话, 会根据当前的压缩文件名称或者压缩文件路径名称创建一个.gspp(7z)的压缩文件出来
m_DestDir = m_SourcePath;
if (!m_FileSuffix.IsEmpty())
{
//创建一个同级文件夹路径
FString RemoveEndStr = TEXT(".");
RemoveEndStr.Append(m_FileSuffix);
m_DestDir.RemoveFromEnd(RemoveEndStr);
}
m_DestDir.Append(TEXT("."));
m_DestDir.Append(DefaultSuffix);
}
try {
bit7z::Bit7zLibrary lib(L"7z.dll");
std::shared_ptr<BitCompressor> compressor = nullptr;
if (m_FileSuffix.Compare(TEXT("gsp")) == 0 || m_FileSuffix.Compare(TEXT("gspp")) == 0 || m_FileSuffix.Compare(TEXT("7z")) == 0)
{
//后缀L".7z"
compressor = std::make_shared<BitCompressor>(lib, BitFormat::SevenZip);
}
else if (m_FileSuffix.Compare(TEXT("zip")) == 0)
{
//后缀L".zip"
compressor = std::make_shared<BitCompressor>(lib, BitFormat::Zip);
}
else if (m_FileSuffix.Compare(TEXT("rar")) == 0)
{
//后缀L".rar"
//compressor = std::make_shared<BitCompressor>(lib, BitFormat::Rar5); //Rar5会报错
}
else if (m_FileSuffix.Compare(TEXT("bz2")) == 0)
{
//后缀L".bz2
compressor = std::make_shared<BitCompressor>(lib, BitFormat::BZip2);
}
else if (m_FileSuffix.Compare(TEXT("xz")) == 0)
{
//后缀L".xz
compressor = std::make_shared<BitCompressor>(lib, BitFormat::Xz);
}
else if (m_FileSuffix.Compare(TEXT("wim")) == 0)
{
//后缀L".wim
compressor = std::make_shared<BitCompressor>(lib, BitFormat::Wim);
}
else if (m_FileSuffix.Compare(TEXT("tar")) == 0)
{
//后缀L".tar
compressor = std::make_shared<BitCompressor>(lib, BitFormat::Tar);
}
else if (m_FileSuffix.Compare(TEXT("gz")) == 0)
{
//后缀L".gz
compressor = std::make_shared<BitCompressor>(lib, BitFormat::GZip);
}
bit7z::TotalCallback TotalCB = std::bind(&FSevenZHelper::TotalSizeCB, this, std::placeholders::_1);
bit7z::ProgressCallback ProCB = std::bind(&FSevenZHelper::ProgressCB, this, std::placeholders::_1);
bit7z::FileCallback fc = std::bind(&FSevenZHelper::FileNameCB, this, std::placeholders::_1);
compressor->setTotalCallback(TotalCB);
compressor->setProgressCallback(ProCB);
compressor->setFileCallback(fc);
compressor->setPassword(*Password);
compressor->compressDirectory(*m_SourcePath, *m_DestDir);
compressor->setUpdateMode(true);
}
catch (const BitException& ex) {
//do something with ex.what()...
cout << ex.what() << endl;
F7ZDelegates::SevenZExceptionDelegate.Broadcast(type, m_SourcePath, ex);
if (m_pExpFunc)
{
m_pExpFunc(ex);
}
}
}
//解压
void FSevenZHelper::Extract(ESPSevenZType type, const FString& Src, const FString& Dest, const FString& Password)
{
//Src不能为空
if (Src.IsEmpty()) return;
//1>源文件(压缩文件格式) 你要对其解压
m_SourcePath = FPaths::ConvertRelativePathToFull(Src);
//2>获取文件后缀
GetFileSuffix(Src);
//3>设置目标路径
if (!Dest.IsEmpty())
{
m_DestDir = FPaths::ConvertRelativePathToFull(Dest);
}
else
{
//3>如果目标路径不设置的话, 则会在同级目录创建一个同级文件夹出来
m_DestDir = m_SourcePath;
if (!m_FileSuffix.IsEmpty())
{
//创建一个同级文件夹路径
FString RemoveEndStr = TEXT(".");
RemoveEndStr.Append(m_FileSuffix);
m_DestDir.RemoveFromEnd(RemoveEndStr);
}
else
{
//后缀读不到, 解压到桌面上
m_DestDir = URegionRiskReadLibrary::GetUserDesktopFullPath();
m_DestDir = FPaths::ConvertRelativePathToFull(m_DestDir);
}
}
try {
bit7z::Bit7zLibrary lib(L"7z.dll");
std::shared_ptr<bit7z::BitExtractor> extractor = nullptr;
if (m_FileSuffix.Compare(TEXT("gsp")) == 0 || m_FileSuffix.Compare(TEXT("gspp")) == 0 || m_FileSuffix.Compare(TEXT("7z")) == 0)
{
//后缀L".7z"
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::SevenZip);
}
else if (m_FileSuffix.Compare(TEXT("zip")) == 0)
{
//后缀L".zip"
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::Zip);
}
else if (m_FileSuffix.Compare(TEXT("rar")) == 0)
{
//后缀L".rar"
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::Rar5);
}
else if (m_FileSuffix.Compare(TEXT("bz2")) == 0)
{
//后缀L".bz2
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::BZip2);
}
else if (m_FileSuffix.Compare(TEXT("xz")) == 0)
{
//后缀L".xz
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::Xz);
}
else if (m_FileSuffix.Compare(TEXT("wim")) == 0)
{
//后缀L".wim
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::Wim);
}
else if (m_FileSuffix.Compare(TEXT("tar")) == 0)
{
//后缀L".tar
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::Tar);
}
else if (m_FileSuffix.Compare(TEXT("gz")) == 0)
{
//后缀L".gz
extractor = std::make_shared<bit7z::BitExtractor>(lib, bit7z::BitFormat::GZip);
}
bit7z::TotalCallback TotalCB = std::bind(&FSevenZHelper::TotalSizeCB, this, std::placeholders::_1);
bit7z::ProgressCallback ProCB = std::bind(&FSevenZHelper::ProgressCB, this, std::placeholders::_1);
bit7z::FileCallback fc = std::bind(&FSevenZHelper::FileNameCB, this, std::placeholders::_1);
extractor->setTotalCallback(TotalCB);
extractor->setProgressCallback(ProCB);
extractor->setFileCallback(fc);
extractor->setPassword(*Password);
extractor->extract(*m_SourcePath, *m_DestDir);
}
catch (const BitException& ex) {
//do something with ex.what()...
cout << ex.what() << endl;
F7ZDelegates::SevenZExceptionDelegate.Broadcast(type, m_SourcePath, ex);
if (m_pExpFunc)
{
m_pExpFunc(ex);
}
}
}
void FSevenZHelper::SetProgressCB(ProgressCallback upcFunc)
{
this->m_pProgressFunc = upcFunc;
}
void FSevenZHelper::SetBitExceptionCallback(BitExceptionCallback ExceptionFunc)
{
this->m_pExpFunc = ExceptionFunc;
}
void FSevenZHelper::SetFileNameCurOperatorCallback(MyFileCallback ufcFunc)
{
this->m_pFileFunc = ufcFunc;
}
1.2.3>使用介绍
压缩支持的格式:
7z、zip、bz2、xz、wim、tar、gz
解压支持的格式:
7z、zip、rar、bz2、xz、wim、tar、gz
进度是double: 0到1
异常是char*: 用ex.what()
压缩暂时不支持.rar的压缩,我代码里面没有做弹框提示,其实建议大家改下代码,做个明确提示。
因为我这个是在UE4引擎里扩展的,所以内部增加了一个UE4的广播。如果你是C++项目的话,将里面的 F7ZDelegates 类的相关内容删除掉,将FString改成std::string,头文件包含目录改一下。
我里面还有个字符串叫gspp,这个是我们自定义的后缀,大家也可以修改。其实对外封装使用来讲,我不该里面写这些格外的格式。大家拿过去自己修改一下吧。
我只是搭建了个基础模板,大家再自己丰富吧。
要是想自己再丰富其他格式其实也是支持的,详情可以看
bit7z-3.1.3\includebitformat.hpp 这个文件,里面定义了很多格式。
解压代码+解压进度监听 +解压异常监听
同样下面例子代码用是UE4智能指针,同理你改成C++11智能指针shared_ptr是一样的。
C++11
std::shared_ptr<FSevenZHelper> p7Z = std::make_shared<FSevenZHelper>();
UE4C++
//新建一个7zHelper类
TSharedPtr<FSevenZHelper> p7Z = MakeShared<FSevenZHelper>();
if (p7Z.Get())
{
//解压
{
//设置进度回调
p7Z->SetprogressCallback(std::bind(&USPGameInstance::progressCallback, this, std::placeholders::_1));
//设置异常回调
p7Z->SetBitExceptionCallback(std::bind(&USPGameInstance::BitExceptionCallback, this, std::placeholders::_1));
//执行解压 将TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian.gspp") 用密码password解压到D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian文件夹
p7Z->CompressDecompression(ESPSevenZType::OpType_Decompression, TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian.gspp"), TEXT(""), TEXT("password"));
}
压缩代码+压缩进度监听+压缩异常监听
//新建一个7zHelper类
TSharedPtr<FSevenZHelper> p7Z = MakeShared<FSevenZHelper>();
if (p7Z.Get())
{
//压缩
{
//设置进度回调
p7Z->SetprogressCallback(std::bind(&USPGameInstance::progressCallback, this, std::placeholders::_1));
//设置异常回调
p7Z->SetBitExceptionCallback(std::bind(&USPGameInstance::BitExceptionCallback, this, std::placeholders::_1));
//执行压缩 将TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian") 用密码password压缩成TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian.gspp")文件
p7Z->CompressDecompression(ESPSevenZType::OpType_Compress, TEXT("D:\\GSP交付\\西安广联达\\WindowsNoEditor\\Engine7zTianhuajian"), TEXT(""), TEXT("password"));
}
}
到这就结束了,谢谢。
谢谢,创作不易,大侠请留步… 动起可爱的双手,点个赞再走呗
ღ( ´・ᴗ・` )比心<( ̄︶ ̄)>*
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)