zlib是最常用的压缩与解压缩的库,对于服务器来说想要提高数据的收发功能就需要对数据进行压缩,压缩的另一个好处是让数据加密,也能减少网络传输的时间,所以本章就讲解zlib的基础功能压缩与解压缩。

zlib的安装

zlib下载链接: zlib
zlib的编译,我是用的是qt编译(qt编译的好处是能多个平台都按照相同流程编译)
1.
在这里插入图片描述
2.去到zlib的源码位置选cmakelist.txt然后点open
在这里插入图片描述
3.
在这里插入图片描述
4.
在这里插入图片描述
5.
在这里插入图片描述
6.查看编译好后的路径
在这里插入图片描述
在这里插入图片描述

![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/405e79fbcc6a4b969cb195aee30f9fe7.png

zlib封装成一个类

封装成类的好处是我们处理数据时不需要纠结分配多大的内存,只需要业务代码即可,减少不必要的操作减少风险。也能对代码更好的管理和通用。

#include <zlib.h>
#include <string>
#include <vector>
#include <fstream>
#include <stdexcept>
#include <cassert>

class ZlibWrapper {
public:
    // 压缩级别范围验证 [4,9]
    explicit ZlibWrapper(int level = 6, int strategy = Z_DEFAULT_STRATEGY)
        : m_level(level), m_strategy(strategy) {
        if (m_level < Z_NO_COMPRESSION || m_level > Z_BEST_COMPRESSION) {
            throw ZlibError("无效的压缩级别");
        }
    }
   ~ZlibWrapper()
    {
        if(m_ZlibWrapper!=nullptr)
        {
            delete m_ZlibWrapper;
        }
    }
    //================ 文件压缩/解压 ================
    void compressFile(const std::string& inputPath, const std::string& outputPath) {
        std::ifstream in(inputPath, std::ios::binary);
        std::ofstream out(outputPath, std::ios::binary);
        if (!in || !out) throw ZlibError("文件打开失败");

        initCompression();
        constexpr size_t CHUNK = 16384;
        std::vector<Bytef> in_buf(CHUNK), out_buf(CHUNK);
        int flush;

        do {
            in.read(reinterpret_cast<char*>(in_buf.data()), CHUNK);
            m_stream.avail_in = static_cast<uInt>(in.gcount());
            m_stream.next_in = in_buf.data();
            flush = in.eof() ? Z_FINISH : Z_NO_FLUSH;

            do {
                m_stream.avail_out = CHUNK;
                m_stream.next_out = out_buf.data();
                checkZlibResult(deflate(&m_stream, flush), "压缩失败");
                out.write(reinterpret_cast<char*>(out_buf.data()),
                         CHUNK - m_stream.avail_out);
            } while (m_stream.avail_out == 0);
        } while (flush != Z_FINISH);

        deflateEnd(&m_stream);
    }

    void decompressFile(const std::string& inputPath, const std::string& outputPath) {
        std::ifstream in(inputPath, std::ios::binary);
        std::ofstream out(outputPath, std::ios::binary);
        if (!in || !out) throw ZlibError("文件打开失败");

        initDecompression();
        constexpr size_t CHUNK = 16384;
        std::vector<Bytef> in_buf(CHUNK), out_buf(CHUNK);

        do {
            in.read(reinterpret_cast<char*>(in_buf.data()), CHUNK);
            m_stream.avail_in = static_cast<uInt>(in.gcount());
            if (m_stream.avail_in == 0) break;
            m_stream.next_in = in_buf.data();

            do {
                m_stream.avail_out = CHUNK;
                m_stream.next_out = out_buf.data();
                checkZlibResult(inflate(&m_stream, Z_NO_FLUSH), "解压失败");
                out.write(reinterpret_cast<char*>(out_buf.data()),
                         CHUNK - m_stream.avail_out);
            } while (m_stream.avail_out == 0);
        } while (true);

        inflateEnd(&m_stream);
    }

    //================ 字符串操作 ================
    std::string compressString(const std::string& input) {
        uLongf bound = compressBound(input.size());
        std::string output(bound, '\0');
        uLongf outlen = bound;

        checkZlibResult(compress2(
            reinterpret_cast<Bytef*>(&output[0]),  // 允许修改内容
            &outlen,
            reinterpret_cast<const Bytef*>(input.data()),
            input.size(),
            m_level
        ), "字符串压缩失败");

        output.resize(outlen);
        return output;
    }

    std::string decompressString(const std::string& compressed) {
        uLongf destLen = compressed.size() * 10;  // 初始预估解压大小
        std::string output(destLen, '\0');

        int ret;
        do {
            ret = uncompress(
                reinterpret_cast<Bytef*>(&output[0]),
                &destLen,
                reinterpret_cast<const Bytef*>(compressed.data()),
                compressed.size()
            );

            if (ret == Z_BUF_ERROR) {  // 缓冲区不足时扩容
                destLen *= 2;
                output.resize(destLen);
            } else {
                checkZlibResult(ret, "字符串解压失败");
            }
        } while (ret == Z_BUF_ERROR);

        output.resize(destLen);
        return output;
    }

    //================ 分块压缩 ================
    void startChunkedCompression(std::ofstream& outStream) {
        initCompression();
        m_chunkedMode = true;
        m_buffer.reserve(16384);  // 预分配缓冲区
        m_outStream = &outStream;  // 保存输出流引用
    }

    void compressChunk(const char* data, size_t length) {
        if (!m_chunkedMode) throw ZlibError("未启动分块模式");

        m_stream.avail_in = length;
        m_stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));

        do {
            m_stream.avail_out = m_buffer.capacity();
            m_stream.next_out = reinterpret_cast<Bytef*>(m_buffer.data());
            checkZlibResult(deflate(&m_stream, Z_SYNC_FLUSH), "分块压缩失败");

            m_outStream->write(m_buffer.data(),
                m_buffer.capacity() - m_stream.avail_out);
        } while (m_stream.avail_in > 0);
    }

    void endChunkedCompression() {
        if (!m_chunkedMode) return;

        // 完成压缩
        m_stream.avail_in = 0;
        int ret;
        do {
            m_stream.avail_out = m_buffer.capacity();
            m_stream.next_out = reinterpret_cast<Bytef*>(m_buffer.data());
            ret = deflate(&m_stream, Z_FINISH);
            m_outStream->write(m_buffer.data(),
                m_buffer.capacity() - m_stream.avail_out);
        } while (ret == Z_OK);

        checkZlibResult(ret, "分块结束失败");
        deflateEnd(&m_stream);
        m_chunkedMode = false;
    }

    void startChunkedDecompression() {
           initDecompression();
           m_decompressPending = false;
           m_inflateFinished = false;
       }

       std::string decompressChunk(const char* data, size_t length) {
           if (m_inflateFinished) return "";

           m_stream.avail_in = length;
           m_stream.next_in = reinterpret_cast<Bytef*>(const_cast<char*>(data));

           std::string output;
           constexpr size_t OUT_CHUNK = 32768; // 32KB输出块
           std::vector<Bytef> out_buf(OUT_CHUNK);

           do {
               m_stream.avail_out = OUT_CHUNK;
               m_stream.next_out = out_buf.data();

               int ret = inflate(&m_stream, Z_SYNC_FLUSH);
               if (ret == Z_STREAM_END) {
                   m_inflateFinished = true;
               } else {
                   checkZlibResult(ret, "分块解压失败");
               }

               // 将解压后的数据添加到输出
               size_t have = OUT_CHUNK - m_stream.avail_out;
               if (have > 0) {
                   output.append(reinterpret_cast<char*>(out_buf.data()), have);
               }

               // 处理未消费完的输入数据(跨块边界情况)
               if (m_stream.avail_in > 0 && ret != Z_STREAM_END) {
                   m_decompressPending = true;
                   m_pendingData.assign(
                       reinterpret_cast<char*>(m_stream.next_in),
                       m_stream.avail_in
                   );
                   break;
               }
           } while (m_stream.avail_out == 0);

           return output;
       }

       void endChunkedDecompression() {
           if (m_decompressPending) {
               // 处理剩余数据(可能需要多次调用)
               std::string finalOutput = decompressChunk(m_pendingData.data(), m_pendingData.size());
               if (!finalOutput.empty()) {
                   // 可根据需求处理最终输出
               }
           }
           inflateEnd(&m_stream);
       }





    //================ 扩展功能 ================
    //设置压缩级别
    void setCompressionLevel(int level) {
        if (level < 0 || level > 9) throw ZlibError("无效压缩级别");
        m_level = level;
    }

    //设置策略
    void setStrategy(int strategy)
    {
        if (strategy < Z_DEFAULT_STRATEGY || strategy > Z_FIXED) {
            throw ZlibError("无效策略");
        }
        m_strategy = strategy;
    }

    static uint32_t calculateAdler32(const std::string& data) {
        return adler32(0L,
            reinterpret_cast<const Bytef*>(data.data()),
            data.size());
    }

    void reserveBuffer(size_t size) {
        m_buffer.clear();
        m_buffer.reserve(size);
    }

    class ZlibError : public std::runtime_error {
        using runtime_error::runtime_error;
    };

static ZlibWrapper*m_ZlibWrapper;
private:
    int m_level;
    int m_strategy;
    z_stream m_stream{};
    std::vector<char> m_buffer;
    bool m_chunkedMode = false;
    std::ofstream* m_outStream = nullptr;

    bool m_decompressPending = false;  // 是否有未处理的输入数据
    bool m_inflateFinished = false;    // 解压是否完成
    std::string m_pendingData;          // 跨块未处理的输入数据

    void initCompression() {//初始化压缩
        m_stream.zalloc = Z_NULL;
        m_stream.zfree = Z_NULL;
        m_stream.opaque = Z_NULL;
        checkZlibResult(deflateInit2(&m_stream, m_level, Z_DEFLATED,
            MAX_WBITS, 8, m_strategy), "压缩初始化失败");
    }

    void initDecompression() {//初始化解压缩
        m_stream.zalloc = Z_NULL;
        m_stream.zfree = Z_NULL;
        m_stream.opaque = Z_NULL;
        checkZlibResult(inflateInit2(&m_stream, MAX_WBITS), "解压初始化失败");
    }

    void checkZlibResult(int code, const char* context) {//异常打印
        if (code >= Z_OK) return;

        std::string msg = context;
        if (m_stream.msg) {
            msg += ": ";
            msg += m_stream.msg;
        }
        throw ZlibError(msg);
    }
};
ZlibWrapper *ZlibWrapper::m_ZlibWrapper=new ZlibWrapper;

测试代码

    char buf[100]="hehahhhao00000000lovework阿斯\n\afafaw\zczczczczc)(c奥的\n\n";
    std::string com=ZlibWrapper::m_ZlibWrapper->compressString(buf);
    std::string decom= ZlibWrapper::m_ZlibWrapper->decompressString(com);

    ZlibWrapper::m_ZlibWrapper->compressFile("./opencv_world310.dll","./0opencv_world310.dll");
    ZlibWrapper::m_ZlibWrapper->decompressFile("./0opencv_world310.dll","./1opencv_world310.dll");
    
  
    printf("%s:%d %s:%d\n",buf,decom.size(),decom.c_str(),com.size());

测试效果

在这里插入图片描述
解压后文件对比
在这里插入图片描述

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐