[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();
}
对于大小文件都可以