当前位置: 首页 > article >正文

[C++]使用cpphttplib的http服务上传和下载

上传(这里指的是客户端向服务端上传文件,编写的服务端的代码)

void onUpload(const httplib::Request &req, httplib::Response &res, const httplib::ContentReader &content_reader)
{
    if(req.is_multipart_form_data()){
        httplib::MultipartFormDataItems files;
        content_reader([&](const httplib::MultipartFormData &file){
                files.push_back(file);
                return true;
        }, 
        [&](const char *data, size_t data_length){
                           files.back().content.append(data, data_length);
                return true;
        });
        for(const auto& file : files){
            if(file.filename.empty()){
                std::cout << file.name << " : " << file.content << std::endl;
            }else{
                //C++ windows下utf-8转gbk
                char* ansiStr = utf8_2_ansi(file.filename.c_str(), nullptr);
                std::string fileName(ansiStr);
                free(ansiStr);
                ansiStr = nullptr;
                std::ofstream ofs("./" + fileName, std::ofstream::trunc | std::ostream::binary);
                if(ofs.is_open()){
                    ofs.write(file.content.c_str(), file.content.size());
                    ofs.close();
                }
            }
        }
    }else{
        //这里好像并没有一个通用的请求头来标识上传文件的文件名,但是可以服务端和客户端约定一个请求头来标识上传的文件名
        std::string fileName = req.get_header_value("fileName");
        //C++ Windows环境下utf8转换gbk编码,才能正确打开文件
        char* ansiStr = utf8_2_ansi(fileName.c_str(), nullptr);
        fileName.assign(ansiStr);
        free(ansiStr);
        ansiStr = NULL;
        std::ofstream ofs("./" + fileName, std::ofstream::trunc | std::ofstream::binary);
        if(!ofs.is_open()){
            res.set_content(R"({"message":"open file failed"})", "appliation/json");
            return;
        }
        //里面的lambda函数可能会调用多次
        content_reader([&](const char *data, size_t data_length){
            std::cout << "data_length : " << data_length << std::endl;
            ofs.write(data, data_length);
            return true;
        });
        ofs.close();
    }
    res.set_content(R"({"message":"upload successed"})", "appliation/json");
}

这里可能由于服务器实现的不同,对于客户端的请求头可能有些要求,下面是Qt环境下的httpclient的实现代码
多条信息上传

    QHttpMultiPart multiparts;
    //cpphttplib必须要求这段
    multiparts.setContentType(QHttpMultiPart::FormDataType);
    QFile file(ui->lineEdit_2->text());
    if(!file.open(QIODevice::ReadOnly)){
        qDebug() << QStringLiteral("读取文件失败");
        return;
    }
    QFileInfo fileInfo(ui->lineEdit_2->text());
    QString fileName = fileInfo.fileName();
    QHttpPart filePart;
    filePart.setBodyDevice(&file);
    filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"\";filename=\"%1\"").arg(fileName).toUtf8());
    filePart.setHeader(QNetworkRequest::ContentTypeHeader, "application/octet-stream");
    QString name = ui->lineEdit_3->text();
    QString value = ui->lineEdit_4->text();
    QHttpPart textPart;
    //不要少写
    textPart.setHeader(QNetworkRequest::ContentDispositionHeader, QStringLiteral("form-data; name=\"%1\";filename=\"\"").arg(name).toUtf8());
    textPart.setHeader(QNetworkRequest::ContentTypeHeader, "text/plain");
    textPart.setBody(value.toUtf8());
    multiparts.append(textPart);
    multiparts.append(filePart);
    QNetworkAccessManager manager;
    QEventLoop loop;
    QNetworkRequest request(QUrl("http://192.168.6.2:8530/upload"));
    connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    QNetworkReply *reply = manager.post(request, &multiparts);
    loop.exec();
    if(reply){
        qDebug() << reply->readAll();
    }

单个文件上传

    QByteArray fileContent;
    QFile file(ui->lineEdit->text());
    if(file.open(QIODevice::ReadOnly)){
        fileContent = file.readAll();
        file.close();
    }else{
        qDebug() << QStringLiteral("读取文件失败");
        return;
    }
    QFileInfo fileInfo(ui->lineEdit->text());
    QString fileName = fileInfo.fileName();
    QNetworkAccessManager manager;
    QEventLoop loop;
    connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    QNetworkRequest request(QUrl("http://192.168.6.2:8530/upload"));
    request.setRawHeader("fileName", fileName.toUtf8());
    request.setRawHeader("content-type", "application/octet-stream");
    QNetworkReply* reply = manager.post(request, fileContent);
    loop.exec();
    if(reply){
        qDebug() << reply->readAll();
        reply->deleteLater();
    }

下载 服务端提供服务让客户端来下载

较大的文件

void onDownload(const httplib::Request &req, httplib::Response &res)
{
    std::ifstream ifs("D:/all-cases/uploadHttpDemo/build/Debug/Git-2.45.2-64-bit.exe", std::ifstream::binary);
    unsigned long long totalCount = 0;
    if(ifs){
        ifs.seekg(0, ifs.end);
        totalCount = ifs.tellg();
        ifs.seekg(0, ifs.beg);
    }else{
        res.set_content(R"({"msg":"open file failed"})", "application/json");
        return;
    }
    if(totalCount > 0){
        char* data = new char[totalCount] {};
        ifs.read(data, totalCount);
        ifs.close();
        const unsigned long long chunkedSize = 1024 * 1024 * 10;
        res.set_header("Content-Disposition", "attachment; filename=\"Git-2.45.2-64-bit.exe\"");
        res.set_chunked_content_provider("multipart/form-data", [data,totalCount,chunkedSize](size_t offset, httplib::DataSink &sink)->bool{
            //如果文件大小小于chunkedSize, 直接一次性写完
            if(totalCount <= chunkedSize){
                sink.write(data + offset, totalCount);
                sink.done();
                return true;
            }
            //最后一次写
            if(offset + chunkedSize >= totalCount){
                sink.write(data + offset, totalCount - offset);
                sink.done();
                return true;
            }else{
                //每次写 chunkedSize大小的数据
                sink.write(data + offset, chunkedSize);
            }
            }, [&data](bool success) {
                delete[]data;
                data = nullptr;
            });
    }
}

较小的文件

void onSmallFile(const httplib::Request &req, httplib::Response &res)
{
    std::ifstream ifs("D:/all-cases/uploadHttpDemo/build/Debug/如果爱忘了.mp3", std::ifstream::binary);
    if(ifs){
        ifs.seekg(0, std::ifstream::end);
        auto totalCount = ifs.tellg();
        ifs.seekg(0, std::ifstream::beg);
        char* data = new char[totalCount]{};
        ifs.read(data, totalCount);
        ifs.close();
        //编码一下否则下载的文件名会乱码
        const char* type = "inline; filename=\"如果爱忘了.mp3\"";
        char* utf8Type = ansi_2_utf8(type, nullptr);
        res.set_header("Content-Disposition", utf8Type);
        free(utf8Type);
        res.set_content(data, totalCount, "audio/mp3");
        delete[] data;
        data = nullptr;
    }else{
        res.set_content(R"({"msg":"open file failed"})", "application/json");
    }
}

httpclient下载代码使用的是Qt环境

    QNetworkAccessManager manager;
    QEventLoop loop;
    QNetworkRequest request(QUrl("http://192.168.110.173:8530/downLoad"));
    connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    QNetworkReply *reply = manager.get(request);
    loop.exec();
    QString fileName;
    QString str = reply->header(QNetworkRequest::ContentDispositionHeader).toString();
    QRegularExpression reg("(?<=filename=\").+(?=\")");
    QRegularExpressionMatch match = reg.match(str);
    if(match.hasMatch()){
       fileName = match.captured();
    }
    qDebug() << "fileName : " << fileName;
    if(reply){
        QFile file("./" + fileName);
        if(file.open(QIODevice::WriteOnly | QIODevice::Truncate)){
            file.write(reply->readAll());
            file.close();
        }else{
            qDebug() << QStringLiteral("打开文件失败");
        }
        reply->deleteLater();
    }

对于大小文件都可以


http://www.kler.cn/a/379957.html

相关文章:

  • vue3 怎么判断数据列是否包某一列名
  • 【双指针】【数之和】 LeetCode 633.平方数之和
  • GR2——在大规模视频数据集上预训练且机器人数据上微调,随后预测动作轨迹和视频(含GR1详解)
  • Python毕业设计选题:基于Hadoop的租房数据分析系统的设计与实现
  • 分享一些做事的原则
  • 【学术论文投稿】探索嵌入式硬件设计:揭秘智能设备的心脏
  • golang运行某个类下所有的基准测试
  • 得物多模态大模型在重复商品识别上的应用和架构演进
  • Django ORM详解: model转字典的几种方法
  • 1.3 初探OpenCV贡献库
  • c++中string底层实现之SSO
  • DMFLDR数据载入使用实践
  • 【Git】Git 远程仓库命令详解
  • three.js 实现 css2d css3d效果 将 二维Dom 和 三维场景结合
  • Oracle 第18章:分区技术
  • 代理IP地址和端口是什么?怎么进行设置?
  • 嵌入式开发之文件I/O-函数
  • 在区块链技术中,什么是权益证明(PoS)?
  • 从 TCP 友好性看传输优化
  • 快速入门CSS
  • flutter dart mixin 姿势
  • 【VS+QT】联合开发踩坑记录
  • 【毫米波雷达(七)】自动驾驶汽车中的精准定位——RTK定位技术
  • 【视频】OpenCV:识别颜色、绘制轮廓
  • Docker 部署RocketMQ
  • SOLIDWORKS 2025加快装配体设计 确保可制造性