C++ web框架 Paozhu 1.14.0 发布,加入html生成pdf文件,增加excel xlsx读写程序

C++ web框架 Paozhu 1.14.0 发布,加入html生成pdf文件, 目前全球几乎唯一C++语言开源功能,速度非常快,自带images处理,生成二维码模块为内置,不需要第三方,因为paozhu已经有了自己原生的图像处理类,目前支持jpg png图片,足够常规web开发使用

paozhu子项目webpdf,是html svg images生成pdf功能,灵感来自fpdf.php,然后重构为c++代码,并且支持ttf otf字体,并优化了程序。

增加excel xlst读写程序,可以实现excel读写功能,目前测试支持office2006 macos number libreoffice生成的xlsx格式

html 转 pdf 文件例子

#include "httppeer.h"
#include "serverconfig.h"
#include "server_localvar.h"
#include "test_webpdf.h"
#include "func.h"
#include <memory>
#include <string>
#ifdef ENABLE_PDF
#include "webpdf.h"
#endif// ENABLE_PDF
namespace http
{
namespace fs = std::filesystem;    
//@urlpath(null,test_webpdf)
std::string test_webpdf(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_PDF
    server_loaclvar &static_server_var = get_server_global_var();

    if (static_server_var.config_path.size() < 5)
    {
        client << "<p> static_server_var.config_path empty </p>";
        return "";
    }

    std::string file_conf = dir_name(static_server_var.config_path);

    if (file_conf.size() > 0 && file_conf.back() != '/')
    {
        file_conf.push_back('/');
    }
    file_conf.append("docs/");
    
    if(peer->pathinfos.size() == 2 && peer->pathinfos[1] == "cn") 
    {    
        try 
        {
            std::string html_file = file_conf +  "webpdf_tutorial.html";
            std::string pdf_file  = file_conf +  "webpdf_tutorial.pdf";
            // OTF (bare CID-keyed CFF) subsetting path. content_otf.html uses
            // the family name "SourceHanSerifSC-Light" directly.
            std::string font_file = "AlibabaPuHuiTi-Light.otf";
            std::string font_name = "AlibabaPuHuiTi-Light";

    
            fs::path html_path = fs::absolute(html_file);
            fs::path pdf_path   = fs::absolute(pdf_file);
            fs::path font_dir   = fs::absolute(file_conf + "font");

            std::ifstream f(html_file, std::ios::binary);
            if (!f.is_open())
            {
                client << "Cannot open file: " << html_file;
                return "";
            }
                
            // 获取文件大小
            f.seekg(0, std::ios::end);
            std::streamsize size = f.tellg();
            f.seekg(0, std::ios::beg);

            std::string html;
            html.resize(static_cast<size_t>(size));
            if (size > 0 && !f.read(html.data(), size)) {
                client << "Failed to read file: " << html_file;
                return "";
            }

            auto pdf = std::make_unique<pz::webpdf>();
            pdf->setImagesPath(file_conf);
            pdf->AddPage();
            pdf->AddFont(font_name,"",font_file,font_dir.string());
            pdf->SetFont(font_name,"",14);
            fs::current_path(fs::absolute(html_file).parent_path());
            pdf->WriteHTML(html);

            peer->type("application/pdf");
            pdf->Output("F", pdf_path.string());
            peer->output = pdf->Output("D", pdf_path.string());
            //peer->output = pdf->move_result();
            
            
        } catch (const std::exception& e) {
            client << "Error: " << e.what();
            return "";
        }
    }
    else
    {
        try 
        {
            std::string html_file = file_conf + "webpdf_tutorial_en.html";
            std::string pdf_file  = file_conf + "webpdf_tutorial_en.pdf";
            std::string font_file = "AlibabaSans-Regular.otf";
            std::string font_name = "AlibabaSans-Regular";

            // Resolve everything to absolute paths up front, before we change the
            // working directory to the HTML's location for relative asset lookups.
            fs::path html_path = fs::absolute(html_file);
            fs::path pdf_path   = fs::absolute(pdf_file);
            fs::path font_dir   = fs::absolute(file_conf + "font");

            std::ifstream f(html_file, std::ios::binary);
            if (!f.is_open())
            {
                client << "Cannot open file: " << html_file;
                return "";
            }
                
            // 获取文件大小
            f.seekg(0, std::ios::end);
            std::streamsize size = f.tellg();
            f.seekg(0, std::ios::beg);

            std::string html;
            html.resize(static_cast<size_t>(size));
            if (size > 0 && !f.read(html.data(), size)) {
                client << "Failed to read file: " << html_file;
                return "";
            }

            auto pdf = std::make_unique<pz::webpdf>();
            pdf->setImagesPath(file_conf);
            pdf->AddFont(font_name, "", font_file, font_dir.string());
            pdf->SetFont(font_name, "", 14);

            // Resources (images, etc.) in the HTML are relative to the HTML file.
            //fs::current_path(html_path.parent_path());
 
            pdf->WriteHTML(html);
            peer->type("application/pdf");
            pdf->Output("F", pdf_path.string());
            peer->output = pdf->Output("D", pdf_path.string());
            //peer->output = pdf->move_result();
            
        } catch (const std::exception& e) {
            std::cerr << "Error: " << e.what() << std::endl;
            return "";
        }
    }
#else
    client << "<p>Please: cmake .. -DENABLE_PDF=ON </p>";
#endif// ENABLE_PDF

    return "";
}

//@urlpath(null,test_ttfpdf)
std::string test_otfpdf(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_PDF
    server_loaclvar &static_server_var = get_server_global_var();

    if (static_server_var.config_path.size() < 5)
    {
        client << "<p> static_server_var.config_path empty </p>";
        return "";
    }

    std::string file_conf = dir_name(static_server_var.config_path);

    if (file_conf.size() > 0 && file_conf.back() != '/')
    {
        file_conf.push_back('/');
    }
    file_conf.append("docs/");
    
    if(peer->pathinfos.size() == 2 && peer->pathinfos[1] == "cn") 
    {    
        try 
        {
            std::string html_file = file_conf +  "webpdf_tutorial.html";
            std::string pdf_file  = file_conf +  "webpdf_tutorial_ttf.pdf";
            // OTF (bare CID-keyed CFF) subsetting path. content_otf.html uses
            // the family name "SourceHanSerifSC-Light" directly.
            std::string font_file = "AlibabaPuHuiTi-Light.ttf";
            std::string font_name = "AlibabaPuHuiTi-Light";

    
            fs::path html_path = fs::absolute(html_file);
            fs::path pdf_path   = fs::absolute(pdf_file);
            fs::path font_dir   = fs::absolute(file_conf + "font");

            std::ifstream f(html_file, std::ios::binary);
            if (!f.is_open())
            {
                client << "Cannot open file: " << html_file;
                return "";
            }
                
            // 获取文件大小
            f.seekg(0, std::ios::end);
            std::streamsize size = f.tellg();
            f.seekg(0, std::ios::beg);

            std::string html;
            html.resize(static_cast<size_t>(size));
            if (size > 0 && !f.read(html.data(), size)) {
                client << "Failed to read file: " << html_file;
                return "";
            }

            auto pdf = std::make_unique<pz::webpdf>();
            pdf->setImagesPath(file_conf);
            pdf->AddPage();
            pdf->AddFont(font_name, "", font_file, font_dir.string());
            pdf->SetFont(font_name, "", 14);

            // Resources (images, etc.) in the HTML are relative to the HTML file.
            fs::current_path(html_path.parent_path());
 
            pdf->WriteHTML(html);

            peer->type("application/pdf");
            pdf->Output("F", pdf_path.string());
            peer->output = pdf->Output("D", pdf_path.string());
            //peer->output = pdf->move_result();
            
            
        } catch (const std::exception& e) {
            client << "Error: " << e.what();
            return "";
        }
    }
    else
    {
        try 
        {
            std::string html_file = file_conf + "webpdf_tutorial_en.html";
            std::string pdf_file  = file_conf + "webpdf_tutorial_en_ttf.pdf";
            std::string font_file = "AlibabaSans-Regular.ttf";
            std::string font_name = "AlibabaSans-Regular";

            // Resolve everything to absolute paths up front, before we change the
            // working directory to the HTML's location for relative asset lookups.
            fs::path html_path = fs::absolute(html_file);
            fs::path pdf_path   = fs::absolute(pdf_file);
            fs::path font_dir   = fs::absolute(file_conf + "font");

            std::ifstream f(html_file, std::ios::binary);
            if (!f.is_open())
            {
                client << "Cannot open file: " << html_file;
                return "";
            }
                
            // 获取文件大小
            f.seekg(0, std::ios::end);
            std::streamsize size = f.tellg();
            f.seekg(0, std::ios::beg);

            std::string html;
            html.resize(static_cast<size_t>(size));
            if (size > 0 && !f.read(html.data(), size)) {
                client << "Failed to read file: " << html_file;
                return "";
            }

            auto pdf = std::make_unique<pz::webpdf>();
            pdf->setImagesPath(file_conf);
            pdf->AddFont(font_name, "", font_file, font_dir.string());
            pdf->SetFont(font_name, "", 14);

            // Resources (images, etc.) in the HTML are relative to the HTML file.
            fs::current_path(html_path.parent_path());

            pdf->WriteHTML(html);
            peer->type("application/pdf");
            pdf->Output("F", pdf_path.string());
            peer->output = pdf->Output("D", pdf_path.string());
            //peer->output = pdf->move_result();
            
        } catch (const std::exception& e) {
            std::cerr << "Error: " << e.what() << std::endl;
            return "";
        }
    }
#else
    client << "<p>Please: cmake .. -DENABLE_PDF=ON </p>";
#endif// ENABLE_PDF

    return "";
}

}// namespace http

 

excel xlsx读写程序例子

 

#include "httppeer.h"
#include "serverconfig.h"
#include "server_localvar.h"
#include "test_pzexcel.h"
#include "func.h"
#include <memory>
#include <string>
#ifdef ENABLE_EXCEL
#include "pzexcel.h"
#endif// ENABLE_EXCEL
namespace http
{
//@urlpath(null,test_pzexcel)
std::string test_pzexcel(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_EXCEL
    server_loaclvar &static_server_var = get_server_global_var();

    if (static_server_var.config_path.size() < 5)
    {
        client << "<p> static_server_var.config_path empty </p>";
        return "";
    }

    std::string file_conf = dir_name(static_server_var.config_path);

    if (file_conf.size() > 0 && file_conf.back() != '/')
    {
        file_conf.push_back('/');
    }
    file_conf.append("docs/");

    std::string zipfile;

    zipfile = file_conf + "tt.xlsx";

    client << "<p>=== Reading " << zipfile << " ===</p></p>";

    pz::excel pza;
    if (!pza.read(zipfile))
    {
        client << "<p>Read error: " << pza.error_msg << "</p>";
        return "";
    }

    client << "<p>\n---------------------\n</p>";
    client << "<p>pz_excel shared_strings size:" << pza.shared_strings.size() << "</p>";
    client << "<p>\n---------------------\n</p>";
    for (unsigned int i = 0; i < pza.shared_strings.size(); i++)
        client << i << ":" << pza.shared_strings[i] << "</p>";

    client << "<p>\n---------------------\n</p>";
    client << "<p>pz_excel sheets size:" << pza.sheets.size() << "</p>";
    client << "<p>\n---------------------\n</p><pre>";

    for (unsigned int i = 0; i < pza.sheets.size(); i++)
    {
        client << i << ":" << pza.sheets[i].name;
        client << i << ":" << pza.sheets[i].sheet_index;

        client << i << "max_row:" << pza.sheets[i].rows;
        client << i << "max_col:" << pza.sheets[i].cols;
    }

    client << "</pre><p>\n---------------------\n</p>";
    for (auto &[first, second] : pza.workbook_mapfiles)
        client << first << " " << second;

    pza.setActiveSheet(1);
    client << "<p>A1: " << pza.getCell<std::string>("A1") << "</p>";
    client << "<p>B2: " << pza.getCell<std::string>("B2") << "</p>";
    client << "<p>B4: " << pza.getCell<std::string>("B4") << "</p>";
    client << "<p>C4: " << pza.getCell<std::string>("C4") << "</p>";

    client << "<p>A5: " << pza.getCell<double>("A5") << "</p>";
    client << "<p>B5: " << pza.getCell<double>("B5") << "</p>";

    client << "<p>\n=== Writing qq.xlsx ===</p>";

    pz::excel pzw;
    pzw.setCell("A1", std::string("姓名"));
    pzw.setCell("B1", std::string("年龄"));
    pzw.setCell("C1", std::string("成绩"));
    pzw.setCell("A2", std::string("张三"));
    pzw.setCell("B2", 25);
    pzw.setCell("C2", 95.5);
    pzw.setCell("A3", std::string("李四"));
    pzw.setCell("B3", 30);
    pzw.setCell("C3", 88.75);
    pzw.setCell("A4", std::string("王五"));
    pzw.setCell("B4", 28);
    pzw.setCell("C4", 92.0);

    pzw.addSheet("Sheet2");
    pzw.setActiveSheet(1);
    pzw.setCell("A1", std::string("第二页数据"));
    pzw.setCell("B2", 12345);
    pzw.setCell("C3", 3.14159);

    std::string qqfile = file_conf + "qq.xlsx";
    if (pzw.write(qqfile))
    {
        client << "<p>Successfully wrote qq.xlsx</p>";
    }
    else
    {
        client << "<p>Write error: " << pzw.error_msg << "</p>";
        return "";
    }

    client << "<p>\n=== Verifying qq.xlsx by reading back ===</p>";
    pz::excel pzv;
    if (pzv.read(qqfile))
    {
        client << "<p>Sheets: " << pzv.sheets.size() << "</p>";
        for (unsigned int i = 0; i < pzv.sheets.size(); i++)
        {
            client << "<p>Sheet " << i + 1 << ": " << pzv.sheets[i].name << ", rows=" << pzv.sheets[i].rows << ", cols=" << pzv.sheets[i].cols << "</p>";
        }
        pzv.setActiveSheet(0);
        client << "<p>A1: " << pzv.getCell<std::string>("A1") << "</p>";
        client << "<p>B2: " << pzv.getCell<int>("B2") << "</p>";
        client << "<p>C2: " << pzv.getCell<double>("C2") << "</p>";
        pzv.setActiveSheet(1);
        client << "<p>Sheet2 A1: " << pzv.getCell<std::string>("A1") << "</p>";
        client << "<p>Sheet2 B2: " << pzv.getCell<int>("B2") << "</p>";
    }
    else
    {
        client << "<p>Verify error: " << pzv.error_msg << "</p>";
    }

    client << "<p>\n=== Reading mm.xlsx and writing mmm.xlsx with setCellColor ===</p>";
    pz::excel pzm;
    std::string mmfile = file_conf + "mm.xlsx";
    if (pzm.read(mmfile))
    {
        client << "<p>Successfully read mm.xlsx" << "</p>";
        client << "<p>Sheets: " << pzm.sheets.size() << "</p>";
        client << "<p>Shared strings: " << pzm.shared_strings.size() << "</p>";
        client << "<p>Fonts count: " << pzm.fonts.size() << "</p>";
        client << "<p>Styles count: " << pzm.styles.size() << "</p>";

        pzm.setActiveSheet(0);
        client << "<p>A1: " << pzm.getCell<std::string>("A1") << "</p>";
        client << "<p>B2: " << pzm.getCell<std::string>("B2") << "</p>";

        pzm.setCellColor("A1", "FFFF0000");
        pzm.setCellColor("B2", "FF00FF00");
        pzm.setCellColor("B4", "FF0000FF");
        pzm.setCellColor("C4", "FFFF2278");

        std::string mmmfile = file_conf + "mmm.xlsx";
        if (pzm.write(mmmfile))
        {
            client << "<p>Successfully wrote mmm.xlsx" << "</p>";

            pz::excel pzverify;
            if (pzverify.read(mmmfile))
            {
                client << "<p>Verified mmm.xlsx read back ok</p>";
                client << "<p>Fonts count: " << pzverify.fonts.size() << "</p>";
                client << "<p>Styles count: " << pzverify.styles.size() << "</p>";
            }
        }
        else
        {
            client << "<p>Write mmm.xlsx error: " << pzm.error_msg << "</p>";
        }
    }
    else
    {
        client << "<p>Read mm.xlsx error: " << pzm.error_msg << "</p>";
    }

    client << "<p>\n=== Reading kk.xlsx (LibreOffice) and writing kk_out.xlsx ===</p>";
    {
        pz::excel pzk;
        std::string kkfile = file_conf + "kk.xlsx";
        if (pzk.read(kkfile))
        {
            client << "<p>Read kk.xlsx OK, shared strings: " << pzk.shared_strings.size() << "</p>";
            client << "<p>A1: " << pzk.getCell<std::string>("A1") << "</p>";
            client << "<p>B2: " << pzk.getCell<std::string>("B2") << "</p>";
            client << "<p>B4: " << pzk.getCell<std::string>("B4") << "</p>";
            client << "<p>C4: " << pzk.getCell<std::string>("C4") << "</p>";
            client << "<p>A5: " << pzk.getCell<double>("A5") << "</p>";
            client << "<p>B5: " << pzk.getCell<double>("B5") << "</p>";

            pzk.setCellColor("A1", "FF0000");
            pzk.setCellColor("B4", "0000FF");

            std::string kkoutfile = file_conf + "kk_out.xlsx";
            if (pzk.write(kkoutfile))
            {
                client << "<p>Wrote kk_out.xlsx OK</p>";
                pz::excel v;
                if (v.read(kkoutfile))
                {
                    client << "<p>Read back kk_out.xlsx OK</p>";
                    client << "<p>A1: " << v.getCell<std::string>("A1") << "</p>";
                    client << "<p>B4: " << v.getCell<std::string>("B4") << "</p>";
                    client << "<p>Fonts with color: </p>";
                    for (size_t i = 0; i < v.fonts.size(); i++)
                    {
                        if (v.fonts[i].color_type == 10)
                            client << "<p>[" << v.fonts[i].color << "] </p>";
                    }
                }
            }
        }
        else
        {
            client << "<p>Read kk.xlsx error: " << pzk.error_msg << "</p>";
        }
    }
#else
    client << "<p>Please: cmake .. -DENABLE_EXCEL=ON </p>";
#endif// ENABLE_EXCEL

    return "";
}

}// namespace http

 

二维码生成例子

#include <chrono>
#include <thread>
#include "httppeer.h"
#include <filesystem>
#ifdef ENABLE_IMAGE
#include "qrcode.h"
#include "pzpng.h"
#endif

#include "testqrcode.h"
namespace http
{
//@urlpath(null,testqrcode)
std::string testqrcode(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
    std::string atxt = client.post["qrcontent"].to_string();
    if (atxt.size() == 0)
    {
        atxt = "qrsting hello world !";
    }
    client << "<p>" << atxt << "</p>";

#ifdef ENABLE_IMAGE
    namespace fs = std::filesystem;
    qr::qrcode q;
    q.text(atxt, qr::Ecc::M, 1);
    image::png img;
    unsigned char scale = 10;
    img.create(q.width()*scale + 40, q.height()*scale + 40, 6, 8);
    img.fillColor({255, 255, 255, 255});
    img.qrdata(q.data,q.width(),q.height(),scale, 20 ,20);
    std::string wwwpath;
    wwwpath.append(client.get_sitepath());
    wwwpath.append("/upload");

    fs::path paths  = wwwpath;
    bool is_success = fs::create_directories(paths);
    if (is_success)
    {
        fs::permissions(paths,
                        fs::perms::owner_all | fs::perms::group_all | fs::perms::others_read,
                        fs::perm_options::add);
    }

    wwwpath.append("/test.png");
    // std::string wwwurl = client.get_hosturl();
    img.save(wwwpath);

    client << "文字      : " << atxt << '\n';
    client << "版本      : " << q.version << '\n';
    client << "黑点大小  : " << q.scale << '\n';
    client << "尺寸      : " << q.width() << " x " << q.height() << '\n';
    client << "q.data 长度: " << q.data.size() << " (= width*height)\n\n";
    //client.output = img.imshow();
    client << "<img src=\"" << "/upload/test.png" << "?token=123456\">";

#endif
    client << "<form method=\"post\" action=\"/testqrcode\"><p><textarea name=\"qrcontent\"></textarea></p><p><input type=\"submit\" name=\"submit2\" value=\"Submit\"></p></form>";
    return "";
}

}// namespace http

 

图片生成处理例子

#include "httppeer.h"
#include "serverconfig.h"
#include "server_localvar.h"
#include "test_pzpng.h"
#include "func.h"
#include <memory>
#include <string>
#ifdef ENABLE_IMAGE
#include "pzjpg.h"
#endif// ENABLE_IMAGE
namespace http
{
//@urlpath(null,test_pzjpg)
std::string test_pzjpg(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_IMAGE
    server_loaclvar &static_server_var = get_server_global_var();

    if (static_server_var.config_path.size() < 5)
    {
        client << "<p> static_server_var.config_path empty </p>";
        return "";
    }

    std::string file_conf = dir_name(static_server_var.config_path);

    if (file_conf.size() > 0 && file_conf.back() != '/')
    {
        file_conf.push_back('/');
    }
    file_conf.append("docs/");

    image::jpg img;
    img.create(800, 600);
    img.fillColor({255, 255, 255});

    img.drawLine(100, 100, 700, 500, {255, 0, 0}, 5);
    img.drawLine(50, 300, 750, 300, {0, 0, 255}, 10);
    img.drawLine(400, 50, 400, 550, {0, 255, 0}, 8);
    client << "<p>开始</p>";
    img.save(file_conf + "line_thickness_test.jpg");
    client << "<p>保存完成</p>";


#else
    client << "<p>Please: cmake .. -DENABLE_IMAGE=ON </p>";
#endif// ENABLE_IMAGE

    return "";
}

//@urlpath(null,test_outjpg)
std::string test_outjpg(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_IMAGE
    // server_loaclvar &static_server_var = get_server_global_var();

    // if (static_server_var.config_path.size() < 5)
    // {
    //     client << "<p> static_server_var.config_path empty </p>";
    //     return "";
    // }

    // std::string file_conf = dir_name(static_server_var.config_path);

    // if (file_conf.size() > 0 && file_conf.back() != '/')
    // {
    //     file_conf.push_back('/');
    // }
    // file_conf.append("docs/");

    image::jpg img;
    img.create(800, 600);
    img.fillColor({255, 255, 255});

    img.drawLine(100, 100, 700, 500, {255, 0, 0}, 5);
    img.drawLine(50, 300, 750, 300, {0, 0, 255}, 10);
    img.drawLine(400, 50, 400, 550, {0, 255, 0}, 8);
    // client << "<p>开始</p>";
    // img.save(file_conf+"line_thickness_test.jpg");
    // client << "<p>保存完成</p>";
    client.type("image/jpg");
    auto vec      = img.imshow();
    client.output = std::string(reinterpret_cast<const char *>(vec.data()), vec.size());


#else
    client << "<p>Please: cmake .. -DENABLE_IMAGE=ON </p>";
#endif// ENABLE_IMAGE

    return "";
}

//@urlpath(null,test_showjpg)
std::string test_showjpg(std::shared_ptr<httppeer> peer)
{
    httppeer &client = peer->get_peer();
#ifdef ENABLE_IMAGE
    server_loaclvar &static_server_var = get_server_global_var();

    if (static_server_var.config_path.size() < 5)
    {
        client << "<p> static_server_var.config_path empty </p>";
        return "";
    }

    std::string file_conf = dir_name(static_server_var.config_path);

    if (file_conf.size() > 0 && file_conf.back() != '/')
    {
        file_conf.push_back('/');
    }
    file_conf.append("docs/images/2388_445.jpg");

    image::jpg img;
    bool isok = img.read(file_conf);
    if(isok)
    {
        client.type("image/jpg");
        auto vec      = img.imshow();
        client.output = std::string(reinterpret_cast<const char *>(vec.data()), vec.size());
    }
    client << "<p>read file error:"<< file_conf <<" </p>";

#else
    client << "<p>Please: cmake .. -DENABLE_IMAGE=ON </p>";
#endif// ENABLE_IMAGE

    return "";
}

}// namespace http

 

1.特性🔥🔥🔥🔥🔥

✅ 1. 自带json编解码不用第三方库,标准json支持
✅ 2. 支持多域名网站
✅ 3. 支持多域名ssl 服务端
✅ 4. 支持http/1.1、http/2协议
✅ 5. 支持WebSocket服务端和客户端,双工收发
✅ 6. 框架自带WebSocket推送,支持定时推送到WebSocket客户端
✅ 7. 支持httpclient get post,同步异步、协程模式,数据采集
✅ 8. 框架自带ORM,使用链接池方式,目前支持MySQL,支持协程和同步模式
✅ 9. 框架自带线程池,和用户代码运行的线程池
✅10. 框架使用asio自带的协程
✅11. 框架特色是I/O 使用协程池 用户代码运行使用线程池,类似GO那种调度,只是针对http请求调度
✅12. 框架支持普通文件gzip、br,并支持缓存到磁盘,下次不用cpu再压缩
✅13. URL、POST和上传文件,解析结果client.get[] client.post[] client.files[]方式获取内容
✅14. 自带sendmail类库
✅15. 生成二维码(qrcode),需要gd、qrencode库
✅16. 插件化编程,热动态更新,使用动态库方式
✅17. 框架内置通用数据缓存模块,ORM结果缓存,提高并发能力
✅18. 框架controller目录注解功能,方便添加URL路由映射,降低入门心智
✅19. 结构和类注解JSON功能,使用json_encode json_decode操作复杂C++结构体和JSON互转,可以参考Wiki
✅20. 提供一个完整admin后台管理框架(见后面图片), 访问URL为 /admin/main
✅21. 支持PHP-FPM fastcgi运行模式,代替Apache做PHP前端,让PHP程序员平稳过渡到 C++ 开发模式
✅22. 内置微信小程序支付功能
✅23. 支持Socket服务端和客户端,支持Socket ssl连接
✅24. 支持RPC服务端和客户端,使用http注册的URL函数
✅25. 框架支持流量限制,60秒内平滑移动平均线二段式流量限制,配置参数在server.conf
✅26. 框架内置ACME协议,自动续订SSL证书并生效
✅27. 整合服务器端OCSP证书装订
✅28. 简单的Excel文件xlsx读写支持
✅29. 内置图片生成和处理,支持jpg png两个类
✅30. 内置HTML生成pdf模块webpdf(本项目子项目)

 

https://github.com/hggq/paozhu

 

posted @ 2026-07-21 15:44  游水小鸡  阅读(8)  评论(0)    收藏  举报