前段时间研究了一下怎样通过cppcms框架读取multipart/form-data类型的请求,由于网上资料较少,只能花了一些时间阅读源代码,了解了基本的使用方法,写个小例子测试一下。
该例子中涉及到的请求数据包括:一般的string、浮点型数组、图像数据,其中,浮点型数组也是以字符串形式传输,图像数据则是以文件形式传输。字符串传输的数组数据,需要还原成原始的浮点型数组,本例中是自己写的字符串解析函数实现,也可以通过其他方式实现,比如转成JSON格式等。
/* 定义一个服务类,用于接收和处理HTTP请求。该服务会接收/formdata这个字段并进行请求的内容解析和处理。 */
class TestCppCms: public cppcms::application {
public:
TestCppCms(cppcms::service &srv) :
cppcms::application(srv)
{
std::cout<<"constructor"<<std::endl;
/* Test for multi-part data request */
dispatcher().assign("/formdata", &TestCppCms::receive_form_data, this);
mapper().assign("formdata", "/formdata");
mapper().root("/");
}
/* 接收数据 */
/* Test for receive form data */
void receive_form_data()
{
/* 输出接收到的请求的请求方法,即POST、GET或者其他方法等 */
std::cout << "request_method: " << request().request_method() << std::endl;
/* 输出请求内容的类型 */
std::cout << "content_type: " << request().content_type() << std::endl;
/* 输出请求内容的长度 */
std::cout << "content_length: " << request().content_length() << std::endl;
/* Get post data in this request */
std::string task_id, service_name, points_str;
std::string key_value_str;
/* 获取key为task_id的value值 */
task_id = request().post("task_id");
std::cout << "task_id = " << task_id << std::endl;
/* 获取service_name对应的value值 */
service_name = request().post("service_name");
std::cout << "service_name = " << service_name << std::endl;
/* 读取points对应的value值,读取到的是一个包含多个浮点坐标的字符串,需要后续解析成实际的浮点数 */
points_str = request().post("points");
std::cout << "points_str: " << points_str << std::endl;
/* 对读取到的浮点数组字符串进行解析,输出数组 */
std::vector<std::string> points_str_vec = parse_array_from_string(points_str, "[,] ");
std::vector<double> points_array;
std::cout << "Points array: " << points_str_vec.size() << " elements." << std::endl;
for(int i = 0; i < points_str_vec.size(); i++)
{
float f = stof(points_str_vec[i]);
points_array.push_back(f);
std::cout << f << " ";
}
std::cout << std::endl;
/* 读取请求中的文件数据 */
/* Get uploaded file in this request */
std::vector<booster::shared_ptr<cppcms::http::file> > files = request().files();
for(int i = 0; i < files.size(); i++)
{
booster::shared_ptr<cppcms::http::file> file_tmp;
file_tmp = files[i];
/* Get file name */
std::string file_name = file_tmp->name(); //files[i]->name;
std::cout << "File " << i << " name: " << file_name << std::endl;
/* 将文件数据读入字符串向量 */
/* Get file data and save file */
file_tmp->data().seekg(0);
std::string img_data = gulp(file_tmp->data());
std::vector<char>vec(img_data.begin(), img_data.end());
/* 从字符串向量中解析出图像文件数据 */
std::cout<<"img_data_size = " << img_data.size() << std::endl;
cv::Mat mat = cv::imdecode(vec, CV_LOAD_IMAGE_COLOR);
char img_name[50];
sprintf(img_name, "recv_%d.jpg", i+1);
cv::imwrite(img_name, mat);
}
/* 向请求端返回响应信息 */
response().out() << "Received images num: " << files.size() << "\n"
<< "task_id = " << task_id << "\n"
<< "service_name = " << service_name << "\n";
}
std::string gulp(std::istream &in)
{
std::string str;
char buffer[4096];
while (in.read(buffer, sizeof(buffer)))
str.append(buffer, sizeof(buffer));
str.append(buffer, in.gcount());
return str;
}
};
其中,以上调用的从字符串解析出浮点数组的函数定义如下,输入参数包含一个输入字符串in_str,和一个分隔符字符串delim,分隔符可包含多种,按任意顺序放入字符串即可。
/* Split string and parse array information */
std::vector<std::string> parse_array_from_string(std::string in_str, const std::string &delim)
{
std::vector<std::string> sub_str_vec;
size_t pos = 0, pos_start = 0, pos_end = 0;
size_t len;
while(pos < in_str.length())
{
pos_start = in_str.find_first_not_of(delim, pos);
std::cout << "pos_start = " << pos_start << std::endl;
if(pos_start != std::string::npos)
{
pos_end = in_str.find_first_of(delim, pos_start + 1);
std::cout << "pos_end = " << pos_end << std::endl;
if(pos_end != std::string::npos)
{
len = pos_end - pos_start; // sub-string lenght
}
else // No delim pattern found
{
len = in_str.length() - pos_start; // sub-string lenght
}
std::string sub_str = in_str.substr(pos_start, len);
sub_str_vec.push_back(sub_str);
pos = pos_start + len + 1;
}
else
{
std::cout << "Hit end of the input string. pos = " << pos << std::endl;
break;
}
}
return sub_str_vec;
}
让我们来测试一下:
int main(int argc, char **argv)
{
/* 读取当前服务的配置文件 */
/* Read config file */
std::ifstream file("config.js");
cppcms::json::value settings;
settings.load(file, true);
/* 启动服务 */
try
{
cppcms::service app(settings);
app.applications_pool().mount(cppcms::applications_factory<TestCppCms>());
app.run();
}
catch(std::exception const &e)
{
std::cerr<<e.what()<<std::endl;
}
}
功能OK。由于原来的实验环境处于非工作状态,暂时无法贴结果,等环境恢复后再补充吧。
附上postman请求截图: