这个 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);
}
}
}
其实就添加了创建文件、写文件、关闭文件 代码。选择一个小的文本文件进行上传:


上传成功的。这个例子只合适上传一般的文本文件,像其他的二进制的文件是用到了其他的例子,这里不作介绍。

本文介绍了如何使用Mongoose实现文件上传功能,包括配置服务器、解决跨平台问题、绕过防火墙限制以及如何从请求中读取并保存上传的文件。
4018

被折叠的 条评论
为什么被折叠?



