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

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

cpp 复制代码
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的实现代码
多条信息上传

cpp 复制代码
    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();
    }

单个文件上传

cpp 复制代码
    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();
    }

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

较大的文件

cpp 复制代码
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;
            });
    }
}

较小的文件

cpp 复制代码
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环境

cpp 复制代码
    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();
    }

对于大小文件都可以

相关推荐
怀澈12217 分钟前
高性能服务器模型之Reactor(单线程版本)
linux·服务器·网络·c++
chnming198740 分钟前
STL关联式容器之set
开发语言·c++
威桑1 小时前
MinGW 与 MSVC 的区别与联系及相关特性分析
c++·mingw·msvc
熬夜学编程的小王1 小时前
【C++篇】深度解析 C++ List 容器:底层设计与实现揭秘
开发语言·数据结构·c++·stl·list
yigan_Eins1 小时前
【数论】莫比乌斯函数及其反演
c++·经验分享·算法
Mr.131 小时前
什么是 C++ 中的初始化列表?它的作用是什么?初始化列表和在构造函数体内赋值有什么区别?
开发语言·c++
阿史大杯茶1 小时前
AtCoder Beginner Contest 381(ABCDEF 题)视频讲解
数据结构·c++·算法
C++忠实粉丝1 小时前
计算机网络socket编程(3)_UDP网络编程实现简单聊天室
linux·网络·c++·网络协议·计算机网络·udp
我们的五年2 小时前
【Linux课程学习】:进程描述---PCB(Process Control Block)
linux·运维·c++
程序猿阿伟2 小时前
《C++ 实现区块链:区块时间戳的存储与验证机制解析》
开发语言·c++·区块链