这个 mongoose 源码是github 上最新的源码,其下载地址为 mongoose github 源码,此篇文章介绍的是其中一个 example: file-upload-html-form,是一个表单形式的文件上传方式,此 example 介绍教程在这里:file_uploads 。源码下载、编译,及调试,其中遇到一些问题,记录如下。
按照其教程,通过浏览器来进行文件的上传,如:
这个意思是要在浏览器地址栏输入:http://localhost:8000 ,这个得根据实际情况进行输入,如果程序是在本机运行,同时你也是通过本机的浏览器的话,这样输入是没问题的。而我的实际情况是,程序在CentOS 系统上运行,但我是在 Window10 上操作文件上传,则这个地址中的 localhost 要相应地修改成 CentOS 系统的 IP 地址。如我的 CentOS 系统 IP 地址为:192.168.2.103,则代码里的 main 函数修改为:
- int main(void)
- {
-
- signal(SIGINT, signal_handler);
- signal(SIGTERM, signal_handler);
-
- struct mg_mgr mgr;
-
- mg_mgr_init(&mgr);
- mg_log_set(MG_LL_DEBUG); // Set log level
- mg_http_listen(&mgr, "http://192.168.2.103:8000", cb, NULL);
-
- while (s_signal == 0)
- {
- mg_mgr_poll(&mgr, 50);
- }
-
-
- mg_mgr_free(&mgr);
-
- return 0;
- }
IP 地址写死或通过参数传入,这个随意。
当代码编译且运行起来后,通过浏览器进行访问 http://192.168.2.103:8000 ,但服务端却没有任何反应,加了一个打印信息,看回调函数是否被调用到:
event type = 2,查看代码是 MG_EV_POLL 事件,回调函数被调用,属于正常的,但访问时为何没有连接的反应呢?原来是请求被 CentOS 防火墙挡住了,需要关闭才能正常访问,我的系统信息是这样的:
关闭命令为:systemctl stop firewalld。关闭后再访问则显示了这个页面:
选择一个小的文本文件上传,打印输出如下:
但实际文件并没有写到服务器端,源码里只是打印了信息,并没有写文件,原源码为:
- // Also, consider changing -DMG_IO_SIZE=SOME_BIG_VALUE to increase IO buffer
- // increment when reading data.
- static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len,
- hm->uri.ptr, (unsigned long) hm->body.len));
- if (mg_http_match_uri(hm, "/upload")) {
- struct mg_http_part part;
- size_t ofs = 0;
- while ((ofs = mg_http_next_multipart(hm->body, ofs, &part)) > 0) {
- MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
- (int) part.name.len, part.name.ptr, (int) part.filename.len,
- part.filename.ptr, (unsigned long) part.body.len));
- }
- mg_http_reply(c, 200, "", "Thank you!");
- } else {
- struct mg_http_serve_opts opts = {.root_dir = "web_root"};
- mg_http_serve_dir(c, ev_data, &opts);
- }
- }
- }
根据它教程里的解释,Mongoose接收该请求,将其完全缓冲到内存中,然后使用mg_http_next_multipart() 函数遍历每个表单字段。上传文件的全部内容被缓存到了 body 里了。
所以我们可以把 body 内容写下来,那就是上传的文件的内容了,修改回调函数代码如下:
- static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
- // printf("event type = %d\n", ev);
- if (ev == MG_EV_HTTP_MSG) {
- struct mg_http_message *hm = (struct mg_http_message *) ev_data;
- MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len,
- hm->uri.ptr, (unsigned long) hm->body.len));
- if (mg_http_match_uri(hm, "/upload")) {
- struct mg_http_part part;
- size_t ofs = 0;
- FILE *fp = NULL;
- char fileName[128] = {0};
-
- while ((ofs = mg_http_next_multipart(hm->body, ofs, &part)) > 0) {
- MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
- (int) part.name.len, part.name.ptr, (int) part.filename.len,
- part.filename.ptr, (unsigned long) part.body.len));
- memcpy(fileName, part.name.ptr, part.name.len);
- if(strstr(fileName, "file") != NULL) {
- memset(fileName, 0, sizeof(fileName));
- memcpy(fileName, part.filename.ptr, part.filename.len);
- if((fp = fopen(fileName, "w+")) == NULL) {
- printf("create file(%s) fail\n", fileName);
- }
- fwrite(part.body.ptr, part.body.len, 1, fp);
- }
- }
- if(fp != NULL) {
- fclose(fp);
- }
- mg_http_reply(c, 200, "", "Thank you!");
- } else {
- struct mg_http_serve_opts opts = {.root_dir = "web_root"};
- mg_http_serve_dir(c, ev_data, &opts);
- }
- }
- }
其实就添加了创建文件、写文件、关闭文件 代码。选择一个小的文本文件进行上传:
上传成功的。这个例子只合适上传一般的文本文件,像其他的二进制的文件是用到了其他的例子,这里不作介绍。