Do you want to upload files of any size simply and joyously? You definitely need Mongoose.

Here’s how you do it, step by step:

First of all, we need a web page with an "Upload button", it might be something like this:

<html><body>Upload example
<form method="POST" action="upload ectype="multipart/form-data">
<input type="file" name="file" />
<br/> <input type="submit" value="Upload"/>
</form></body></html>

Now, we need to implement an event handler. It is usually the Mongoose event handler. Here is how it begins:

static void handle_upload(struct mg_connection *nc, int ev, void *p) {
struct file_writer_data *data = (struct file_writer_data *) nc->user_data;
struct mg_http_multipart_part mp = (struct mg_http_multipart_part)p;

Take a look at the mg_http_multipart_part structure. It contains useful information about the uploading process.

In the handler, we need to take care of the following events:

  • MG_EV_HTTP_PART_BEGIN: new incoming data request, we can do some preparation here. For example, we can open the file.

case MG_EV_HTTP_PART_BEGIN:
.....
data = calloc(1, sizeof(struct file_writer_data));
/* We use temp file, but it is possible to use real file name;
mp->file_name contains it */
data->fp = tmpfile();
data->bytes_written = 0;
....

  • MG_EV_HTTP_PART_DATA: data continues, mp contains pointer to data and its length. Writing to file here.

case MG_EV_HTTP_PART_DATA:
...
fwrite(mp->data.p, 1, mp->data.len, data->fp
....

  • MG_EV_HTTP_PART_END: data finished, time to close file

case MG_EV_HTTP_PART_END:
....
fclose(data->fp);
....

Let’s put it all together:

static void handle_upload(struct mg_connection *nc, int ev, void *p) {
struct file_writer_data *data =
(struct file_writer_data *) nc->user_data;
struct mg_http_multipart_part mp =
(struct mg_http_multipart_part
)p;
switch (ev) {
case MG_EV_HTTP_PART_BEGIN: {
if (data == NULL) {
data = calloc(1, sizeof(struct file_writer_data));
data->fp = tmpfile();
data->bytes_written = 0;
if (data->fp == NULL) {
mg_printf(nc, "%s",
"HTTP/1.1 500 Failed to open a file\r\n"
"Content-Length: 0\r\n\r\n");
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
nc->user_data = (void *) data;
}
break;
}
case MG_EV_HTTP_PART_DATA: {
if (fwrite(mp->data.p, 1,
mp->data.len, data->fp) != mp->data.len) {
mg_printf(nc, "%s",
"HTTP/1.1 500 Failed to write to a file\r\n"
"Content-Length: 0\r\n\r\n");
nc->flags |= MG_F_SEND_AND_CLOSE;
return;
}
data->bytes_written += mp->data.len;
break;
}
case MG_EV_HTTP_PART_END: {
mg_printf(nc,
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
"Connection: close\r\n\r\n"
"Written %ld of POST data to a temp file\n\n",
(long) ftell(data->fp));
nc->flags |= MG_F_SEND_AND_CLOSE;
fclose(data->fp);
free(data);
nc->user_data = NULL;
break;
}
}
}

Don't forget to establish the listening connection:

nc = mg_bind(&mgr, "1234", ev_handler);

And register the http endpoint (this is optional)

mg_register_http_endpoint(nc, "/upload", handle_upload);

That's basically it. Mongoose will handle the rest.

Two more things you need to know:

You can use the same approach for uploading files to embedded devices. Mongoose parses a multipart request on the fly and issues events as we described earlier without waiting for the entire request to be buffered. So, you can upload gigabyte files, even if your device has only a few kilobytes of memory.

And Mongoose can do even more for you. If all you need is to save an uploaded file into a file system (not, let’s say, program a flash chip), you can use mg_file_upload_handler helper function. In this case event handler shapes to:

void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
switch (ev) {
...
case MG_EV_HTTP_PART_BEGIN:
case MG_EV_HTTP_PART_DATA:
case MG_EV_HTTP_PART_END:
mg_file_upload_handler(nc, ev, ev_data, upload_fname);
break;
}
}

This is really simple.

To contact: send us a message or ask on the developer forum.