使用minzi解压zip

简介

miniz,一个轻量的 zlib (RFC 1950) and Deflate (RFC 1951) 实现库。

一种在 Windows 下的实现

miniz 配置

  1. 加入include目录
  2. 项目添加对应文件

头文件

#pragma once

#include <memory>

class MY_Zip {
public:
    MY_Zip();
    virtual ~MY_Zip();
    bool unzip(const char* in, const char* out);
    const char* error();
    int code();
private:
    class Impl;
    std::unique_ptr<Impl> m_pImpl;
};

.h 实现

#include "MY_Zip.h"
#include "miniz.h"
#include <iostream>
#include <string>
#include <vector>
#include <windows.h>
#include <memory>
#include <ShlObj_core.h>


class MY_Zip::Impl {
public:
  std::string errMsg;
  int errCode;
  bool IsDirectory(const std::string& path) {
    std::string name = path;
    return !name.empty() && (name.back() == '/' || name.back() == '\\');
  }

  bool CreateDirectory(const std::string& path) {
    int result = SHCreateDirectoryExW(NULL, string2wstring(path).c_str(), NULL);
    return result == ERROR_SUCCESS || result == ERROR_ALREADY_EXISTS;
  }
private:
  std::string wstring2string(const std::wstring& wstr) {
    int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, NULL, 0, NULL, NULL);
    std::string str(size_needed - 1, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], size_needed, NULL, NULL);
    return str;
  }

  std::wstring string2wstring(const std::string& str) {
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, NULL, 0);
    std::wstring wstr(size_needed - 1, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), -1, &wstr[0], size_needed);
    return wstr;
  }
};

MY_Zip::MY_Zip() : m_pImpl(new Impl)
{
}

MY_Zip::~MY_Zip()
{
}

bool MY_Zip::unzip(const char* in, const char* out)
{
  mz_zip_archive archive{};
  if (!mz_zip_reader_init_file(&archive, in, 0)) {
    m_pImpl->errMsg = "can not open zip file: " + std::string(in);
    std::cerr << m_pImpl->errMsg << std::endl;
    return false;
  }
  int file_cnt = (int)mz_zip_reader_get_num_files(&archive);
  if (file_cnt == 0) {
    m_pImpl->errMsg = "zip file is empty";
    std::cerr << m_pImpl->errMsg << std::endl;
    return false;
  }

  for (int i = 0; i < file_cnt; ++i) {
    mz_zip_archive_file_stat file_stat;
    if (!mz_zip_reader_file_stat(&archive, i, &file_stat)) {
      m_pImpl->errMsg = "can't get file infomation in zip";
      mz_zip_reader_end(&archive);
      return false;
    }

    std::string path = std::string(out) + "\\" + file_stat.m_filename;
    if (m_pImpl->IsDirectory(path)) {
      m_pImpl->CreateDirectory(path);
      continue;
    }

    size_t last_slash = path.find_last_of("/\\");
    if (last_slash != std::string::npos) {
      m_pImpl->CreateDirectory(path.substr(0, last_slash));
    }

    if (!mz_zip_reader_extract_to_file(&archive, i, path.c_str(), 0)) {
      m_pImpl->errMsg = "unzip failed " + path;
      m_pImpl->errCode = archive.m_last_error;
      std::cerr << m_pImpl->errMsg << std::endl;
      mz_zip_reader_end(&archive);
      return false;
    }
  }
  mz_zip_reader_end(&archive);
  m_pImpl->errMsg = "";
  std::cout << "unzip complete." << std::endl;
  return true;
}

const char* MY_Zip::error()
{
  return m_pImpl->errMsg.c_str();
}

int MY_Zip::code()
{
  return m_pImpl->errCode;
}

使用:

string tempLatestFile = ANSItoUTF8(strLatestFile.GetBuffer());
string tempLatestFolder = ANSItoUTF8(strLatestFolder.GetBuffer());
hjZip.unzip(tempLatestFile.c_str(), tempLatestFolder.c_str())

miniz_unzip

其中路径转换:

string UTF8toANSI(const std::string& instr) //utf-8-->ansi
{
    int MAX_STRSIZE = instr.length() * 2 + 2;
    WCHAR* wcharstr = new WCHAR[MAX_STRSIZE];
    memset(wcharstr, 0, MAX_STRSIZE);
    MultiByteToWideChar(CP_UTF8, 0, (char*)instr.data(), -1, wcharstr, MAX_STRSIZE);
    char* charstr = new char[MAX_STRSIZE];
    memset(charstr, 0, MAX_STRSIZE);
    WideCharToMultiByte(CP_ACP, 0, wcharstr, -1, charstr, MAX_STRSIZE, NULL, NULL);
    string charstrtemp(charstr);
    delete []wcharstr;
    delete []charstr;
    return charstrtemp;
}

string ANSItoUTF8(const std::string& instr) //ansi-->utf-8
{
    int MAX_STRSIZE = instr.length() * 2 + 2;
    WCHAR* wcharstr = new WCHAR[MAX_STRSIZE];
    memset(wcharstr, 0, MAX_STRSIZE);
    MultiByteToWideChar(CP_ACP, 0, (char*)instr.data(), -1, wcharstr, MAX_STRSIZE);
    char* charstr = new char[MAX_STRSIZE];
    memset(charstr, 0, MAX_STRSIZE);
    WideCharToMultiByte(CP_UTF8, 0, wcharstr, -1, charstr, MAX_STRSIZE, NULL, NULL);
    CString charstrtemp(charstr);
    delete[] wcharstr;
    delete[] charstr;
    return (LPCSTR)charstrtemp;
}

一种 Linux 下的实现

使用 C++17 的 filesystem

CMakeLists.txt 配置(使用 git clone 获取对应的 miniz 项目):

cmake_minimum_required(VERSION 3.10)
project(test)

set(CMAKE_CXX_STANDARD 17)

add_subdirectory(vendor/miniz)

add_executable(test main.cpp MY_Zip.h MY_Zip.cpp)

target_link_libraries(test miniz)

代码部分改动实现即可:

#include "MY_Zip.h"
#include "miniz.h"
#include <iostream>
#include <string>
#include <memory>
#include <filesystem>

namespace fs = std::filesystem;

class MY_Zip::Impl {
public:
  std::string errMsg;
  int errCode;

  bool IsDirectory(const std::string& path) {
    return !path.empty() && (path.back() == '/' || path.back() == '\\');
  }

  bool CreateDirectory(const std::string& path) {
    std::error_code ec;
    return fs::create_directories(fs::path(path), ec) || ec.value() == 0;
  }
};

MY_Zip::MY_Zip() : m_pImpl(new Impl) {}
MY_Zip::~MY_Zip() {}

bool MY_Zip::unzip(const char* in, const char* out) {
  mz_zip_archive archive{};
  if (!mz_zip_reader_init_file(&archive, in, 0)) {
    m_pImpl->errMsg = "can not open zip file: " + std::string(in);
    std::cerr << m_pImpl->errMsg << std::endl;
    return false;
  }

  int file_cnt = static_cast<int>(mz_zip_reader_get_num_files(&archive));
  if (file_cnt == 0) {
    m_pImpl->errMsg = "zip file is empty";
    std::cerr << m_pImpl->errMsg << std::endl;
    mz_zip_reader_end(&archive);
    return false;
  }

  for (int i = 0; i < file_cnt; ++i) {
    mz_zip_archive_file_stat file_stat;
    if (!mz_zip_reader_file_stat(&archive, i, &file_stat)) {
      m_pImpl->errMsg = "can't get file info in zip";
      mz_zip_reader_end(&archive);
      return false;
    }

    fs::path targetPath = fs::path(out) / file_stat.m_filename;

    if (m_pImpl->IsDirectory(file_stat.m_filename)) {
      m_pImpl->CreateDirectory(targetPath.string());
      continue;
    }

    fs::path parentDir = targetPath.parent_path();
    m_pImpl->CreateDirectory(parentDir.string());

    if (!mz_zip_reader_extract_to_file(&archive, i, targetPath.string().c_str(), 0)) {
      m_pImpl->errMsg = "unzip failed: " + targetPath.string();
      m_pImpl->errCode = archive.m_last_error;
      std::cerr << m_pImpl->errMsg << std::endl;
      mz_zip_reader_end(&archive);
      return false;
    }
  }

  mz_zip_reader_end(&archive);
  m_pImpl->errMsg = "";
  std::cout << "unzip complete." << std::endl;
  return true;
}

const char* MY_Zip::error() {
  return m_pImpl->errMsg.c_str();
}

int MY_Zip::code() {
  return m_pImpl->errCode;
}

test_on_linux


comment: