216 lines
5.5 KiB
C++
216 lines
5.5 KiB
C++
#include "XNCodeZip.h"
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <zip.h>
|
|
#include <cstring>
|
|
#include <functional>
|
|
namespace fs = std::filesystem;
|
|
|
|
int XNCodeZip::Zip(const std::string &srcPath, const std::string &dstPath, std::string &errorMsg)
|
|
{
|
|
// 检查源路径是否存在
|
|
if (!fs::exists(srcPath)) {
|
|
errorMsg = "源路径不存在: " + srcPath;
|
|
return -1;
|
|
}
|
|
|
|
// 确保目标目录存在
|
|
fs::path dstDir = fs::path(dstPath).parent_path();
|
|
if (!fs::exists(dstDir)) {
|
|
fs::create_directories(dstDir);
|
|
}
|
|
|
|
// 打开zip文件
|
|
int err = 0;
|
|
zip_t *zip = zip_open(dstPath.c_str(), ZIP_CREATE | ZIP_TRUNCATE, &err);
|
|
if (zip == nullptr) {
|
|
char errstr[1024];
|
|
zip_error_to_str(errstr, sizeof(errstr), err, errno);
|
|
errorMsg = "无法创建zip文件: " + std::string(errstr);
|
|
return -1;
|
|
}
|
|
|
|
// 递归添加文件和目录
|
|
std::function<void(const fs::path &, const fs::path &)> addToZip =
|
|
[&](const fs::path ¤tPath, const fs::path &relativePath) {
|
|
if (fs::is_directory(currentPath)) {
|
|
// 添加目录
|
|
std::string dirName = relativePath.string() + "/";
|
|
zip_dir_add(zip, dirName.c_str(), ZIP_FL_OVERWRITE);
|
|
|
|
// 递归处理子目录和文件
|
|
for (const auto &entry : fs::directory_iterator(currentPath)) {
|
|
fs::path newRelativePath = relativePath / entry.path().filename();
|
|
addToZip(entry.path(), newRelativePath);
|
|
}
|
|
} else if (fs::is_regular_file(currentPath)) {
|
|
// 添加文件
|
|
std::string fileName = relativePath.string();
|
|
zip_source_t *source = zip_source_file(zip, currentPath.c_str(), 0, 0);
|
|
if (source == nullptr) {
|
|
errorMsg = "无法创建zip源: " + fileName;
|
|
return;
|
|
}
|
|
|
|
if (zip_file_add(zip, fileName.c_str(), source, ZIP_FL_OVERWRITE) < 0) {
|
|
zip_source_free(source);
|
|
errorMsg = "无法添加文件到zip: " + fileName;
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
|
|
// 开始压缩
|
|
fs::path srcDir(srcPath);
|
|
fs::path baseName = srcDir.filename();
|
|
addToZip(srcDir, baseName);
|
|
|
|
// 关闭zip文件
|
|
zip_close(zip);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int XNCodeZip::Unzip(const std::string &srcPath, const std::string &dstPath, std::string &errorMsg)
|
|
{
|
|
// 检查源路径是否存在
|
|
if (!fs::exists(srcPath)) {
|
|
errorMsg = "源路径不存在: " + srcPath;
|
|
return -1;
|
|
}
|
|
|
|
// 确保目标目录存在
|
|
fs::path dstDir = fs::path(dstPath);
|
|
if (!fs::exists(dstDir)) {
|
|
fs::create_directories(dstDir);
|
|
}
|
|
|
|
// 打开zip文件
|
|
int err = 0;
|
|
zip_t *zip = zip_open(srcPath.c_str(), 0, &err);
|
|
if (zip == nullptr) {
|
|
char errstr[1024];
|
|
zip_error_to_str(errstr, sizeof(errstr), err, errno);
|
|
errorMsg = "无法打开zip文件: " + std::string(errstr);
|
|
return -1;
|
|
}
|
|
|
|
// 获取zip文件中的条目数量
|
|
zip_int64_t numEntries = zip_get_num_entries(zip, 0);
|
|
if (numEntries < 0) {
|
|
errorMsg = "无法获取zip文件条目数量";
|
|
zip_close(zip);
|
|
return -1;
|
|
}
|
|
|
|
// 检测根目录名称(去除额外层级)
|
|
std::string rootDirName;
|
|
bool hasRootDir = false;
|
|
|
|
// 检查第一个条目来确定根目录结构
|
|
if (numEntries > 0) {
|
|
struct zip_stat st;
|
|
if (zip_stat_index(zip, 0, 0, &st) >= 0) {
|
|
std::string firstEntry = st.name;
|
|
size_t slashPos = firstEntry.find('/');
|
|
if (slashPos != std::string::npos) {
|
|
rootDirName = firstEntry.substr(0, slashPos);
|
|
hasRootDir = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 遍历并解压所有条目
|
|
for (zip_int64_t i = 0; i < numEntries; ++i) {
|
|
// 获取条目信息
|
|
struct zip_stat st;
|
|
if (zip_stat_index(zip, i, 0, &st) < 0) {
|
|
errorMsg = "无法获取zip条目信息";
|
|
zip_close(zip);
|
|
return -1;
|
|
}
|
|
|
|
// 跳过空条目
|
|
if (st.size == 0 && st.name[strlen(st.name) - 1] == '/') {
|
|
continue;
|
|
}
|
|
|
|
// 构建目标文件路径,去除根目录层级
|
|
std::string relativePath = st.name;
|
|
if (hasRootDir && relativePath.find(rootDirName + "/") == 0) {
|
|
relativePath = relativePath.substr(rootDirName.length() + 1);
|
|
}
|
|
|
|
// 如果去除根目录后路径为空,跳过
|
|
if (relativePath.empty()) {
|
|
continue;
|
|
}
|
|
|
|
fs::path targetPath = dstDir / relativePath;
|
|
|
|
// 如果是目录,创建目录
|
|
if (relativePath[relativePath.length() - 1] == '/') {
|
|
fs::create_directories(targetPath);
|
|
continue;
|
|
}
|
|
|
|
// 确保文件的父目录存在
|
|
fs::create_directories(targetPath.parent_path());
|
|
|
|
// 检查文件是否已存在
|
|
if (fs::exists(targetPath)) {
|
|
// 可以选择跳过、覆盖或询问用户
|
|
// 这里选择覆盖已有文件
|
|
fs::remove(targetPath);
|
|
}
|
|
|
|
// 打开zip条目
|
|
zip_file_t *zipFile = zip_fopen_index(zip, i, 0);
|
|
if (zipFile == nullptr) {
|
|
errorMsg = "无法打开zip条目: " + std::string(st.name);
|
|
zip_close(zip);
|
|
return -1;
|
|
}
|
|
|
|
// 创建目标文件
|
|
std::ofstream outFile(targetPath, std::ios::binary);
|
|
if (!outFile.is_open()) {
|
|
errorMsg = "无法创建目标文件: " + targetPath.string();
|
|
zip_fclose(zipFile);
|
|
zip_close(zip);
|
|
return -1;
|
|
}
|
|
|
|
// 读取并写入文件内容
|
|
char buffer[8192];
|
|
zip_int64_t bytesRead;
|
|
while ((bytesRead = zip_fread(zipFile, buffer, sizeof(buffer))) > 0) {
|
|
outFile.write(buffer, bytesRead);
|
|
if (!outFile.good()) {
|
|
errorMsg = "写入文件失败: " + targetPath.string();
|
|
outFile.close();
|
|
zip_fclose(zipFile);
|
|
zip_close(zip);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// 检查读取是否成功
|
|
if (bytesRead < 0) {
|
|
errorMsg = "读取zip条目失败: " + std::string(st.name);
|
|
outFile.close();
|
|
zip_fclose(zipFile);
|
|
zip_close(zip);
|
|
return -1;
|
|
}
|
|
|
|
// 关闭文件
|
|
outFile.close();
|
|
zip_fclose(zipFile);
|
|
}
|
|
|
|
// 关闭zip文件
|
|
zip_close(zip);
|
|
|
|
return 0;
|
|
} |