Example Path: examples/Web/MultipartFormPost
Multipart Form Post Example
Overview
This NetBurner example demonstrates how to handle multipart/form-data HTTP POST requests on embedded devices. Unlike standard URL-encoded forms, multipart forms can transmit both file content and regular form fields in a single request, making them essential for file upload functionality.
Features
- File upload handling with multipart/form-data encoding
- Mixed content processing (files, text fields, and checkboxes)
- Safe file data extraction from POST requests
- Dynamic response generation with file content display
- Text and binary/hex dump display modes
- HTML entity encoding for safe content display
- Configurable maximum file size (default 500KB)
Key Concepts
Multipart vs URL-Encoded Forms
URL-encoded forms (application/x-www-form-urlencoded):
- Used for simple form data (text fields, selections)
- Data is encoded as key=value pairs separated by &
- Not suitable for file uploads
- Smaller overhead for simple forms
Multipart forms (multipart/form-data):
- Required for file uploads
- Each field is sent as a separate "part" with headers
- Can mix files and regular form fields in one request
- Set with: enctype="multipart/form-data"
POST Event Sequence
When a multipart form is submitted, the NetBurner HTTP server calls your callback function multiple times with different events:
+----------------+
| Form Submitted |
+-------v--------+
|
+-------v--------+
| eStartingPost | Called once at the beginning
| (Initialize) | - Clear variables
+-------v--------+ - Reset state
|
+-------v--------+
| eVariable | Called for each text field
| (name, value) | - Process text inputs
+-------v--------+ - Handle checkboxes
| (may be called multiple times)
+-------v--------+
| eFile | Called for each file input
| (name, struct) | - Read file data from fd
+-------v--------+ - Store filename
| (may be called multiple times)
+-------v--------+
| eEndOfPost | Called once at the end
| (Send response)| - Generate HTML response
+----------------+ - Redirect to results page
Project Structure
MultipartFormPost/
|
+-- src/
| +-- main.cpp Application entry point
| +-- formcode.cpp POST handling and file processing
| +-- htmldata.cpp Auto-generated from html/ directory
|
+-- html/
| +-- index.html Upload form with multipart encoding
| +-- images/
| +-- netburner-logo.gif
|
+-- makefile Build configuration
+-- ReadMe.txt This file
Architecture
System Flow
+----------------+ +----------------+ +------------------+
| | | | | |
| Web Browser | -----> | HTTP Server | -----> | POST Callback |
| | | | | |
| [Upload Form] | | Parse multipart| | MultipartPost |
| enctype= | | boundaries | | CallBack() |
| multipart/ | | Extract parts | | |
| form-data | | | | - eStartingPost |
| | | | | - eVariable |
+----------------+ +----------------+ | - eFile |
| - eEndOfPost |
+--------v---------+
|
+--------v---------+
| |
| GenerateResults |
| Page() |
| |
| - Show fields |
| - Display file |
| |
+------------------+
File Upload Processing
When the eFile event is received, the pValue parameter points to a FilePostStruct containing information about the uploaded file:
+------------------+
| FilePostStruct |
+------------------+
| fd: int | ---> File descriptor for reading content
| pFileName: char* | ---> Original filename from browser
+------------------+
|
v
+------------------+ +------------------+ +------------------+
| | | | | |
| Check
dataavail | -----> |
read(fd, buf, n) | -----> | Store in buffer |
| (fd) | | | | FileRxBuffer[] |
| | | Returns bytes | | |
+------------------+ +------------------+ +------------------+
| |
| +----------------------------+
v v
+------------------+ +------------------+
| | | |
|
close(fd) | | Track FileSize |
| When done | | |
| | | |
+------------------+ +------------------+
int dataavail(int fd)
Check the specified file descriptor to detmeine if data is available to be read.
int read(int fd, char *buf, int nbytes)
Read data from a file descriptor (fd).
int close(int fd)
Close the specified file descriptor and free the associated resources.
Implementation Details
HTML Form Setup
The HTML form MUST include enctype="multipart/form-data" for file uploads:
<form action="upload" enctype="multipart/form-data" method="POST">
<!-- Text fields generate eVariable events -->
<input type="text" name="description" value="">
<!-- File input generates eFile event -->
<input type="file" name="userfile">
<!-- Checkboxes only sent when checked -->
<input type="checkbox" name="show_binary">
<input type="submit" value="Upload">
</form>
POST Callback Registration
Register a callback function to handle POST requests matching a URL pattern:
Implements the HtmlPostVariableListHandler class as a function pointer callback for HTTP POST submiss...
Definition httppost.h:131
Callback Function Structure
int MultipartPostCallBack(int sock, PostEvents event,
const char *pName, const char *pValue)
{
switch (event)
{
case eStartingPost:
FileSize = 0;
memset(description, 0, sizeof(description));
break;
case eVariable:
if (strcmp("description", pName) == 0)
{
strncpy(description, pValue, MAX_LEN - 1);
}
else if (strcmp("show_binary", pName) == 0)
{
showBinary = true;
}
break;
case eFile:
ProcessPostFile(pValue);
break;
case eEndOfPost:
GenerateResultsPage(sock);
break;
}
return 0;
}
Reading File Data
The eFile event provides a FilePostStruct with file information:
void ProcessPostFile(const char *pValue)
{
FilePostStruct *pFps = (FilePostStruct *)pValue;
if (pFps->pFileName != nullptr)
{
strncpy(uploadedFileName, pFps->pFileName, MAX_LEN - 1);
}
{
int remaining = BUFFER_SIZE - FileSize;
int bytesRead =
read(pFps->fd, buffer + FileSize, remaining);
if (bytesRead > 0)
{
FileSize += bytesRead;
}
else
{
break;
}
if (FileSize >= BUFFER_SIZE)
{
break;
}
}
}
Generating the Response
After processing, generate an HTML response in eEndOfPost:
void GenerateResultsPage(int sock)
{
fdprintf(sock,
"<p>Description: %s</p>", description);
fdprintf(sock,
"<p>Filename: %s</p>", uploadedFileName);
fdprintf(sock,
"<p>Size: %d bytes</p>", FileSize);
ShowFileData(sock, showBinary);
}
int fdprintf(int fd, const char *format,...)
Print formatted output to a file descriptor.
int writestring(int fd, const char *str)
Write a null terminated ascii string to the stream associated with a file descriptor (fd)....
void SendHTMLHeader(int sock)
Send a HTML response header.
Comparison with HtmlFormPost Example
+----------------------+---------------------------+---------------------------+
| Feature | HtmlFormPost | MultipartFormPost |
+----------------------+---------------------------+---------------------------+
| Encoding | application/x-www- | multipart/form-data |
| | form-urlencoded | |
+----------------------+---------------------------+---------------------------+
| File uploads | No | Yes |
+----------------------+---------------------------+---------------------------+
| Form attribute | method=post | enctype="multipart/ |
| | | form-data" method="POST" |
+----------------------+---------------------------+---------------------------+
| File event | Not used | eFile with FilePostStruct |
+----------------------+---------------------------+---------------------------+
| Use case | Simple text forms | File uploads, mixed |
| | | content forms |
+----------------------+---------------------------+---------------------------+
Memory Considerations
+---------------------------+------------------+
| Component | Size |
+---------------------------+------------------+
| File buffer | 500 KB (config) |
| Text field buffers | 128 bytes each |
| Filename buffer | 128 bytes |
+---------------------------+------------------+
Total static allocation: ~500 KB + overhead
- No dynamic memory allocation used
- Large files are truncated to buffer size
- Adjust FILE_BUFFER_SIZE in formcode.cpp for different limits
Console Output
When a file is uploaded, the console displays:
Application: Multipart Form Post Example
NNDK Revision: 3.x.x Build xxxx
Access the web interface at: http:
--- Starting new multipart POST ---
Variable: uploader_name = "John Doe"
Variable: description = "Test file upload"
Variable: show_binary = "on"
File upload received: userfile
Uploaded file name: example.txt
File size: 1234 bytes
--- End of POST ---
Summary: Uploader=John Doe, Description=Test file upload, FileSize=1234
Common Issues and Solutions
File Not Uploading
Symptom: eFile event never fires, only eVariable events received
Cause: Missing enctype attribute on form
Solution: Add enctype="multipart/form-data" to the form tag
<!-- Wrong -->
<form action="upload" method="POST">
<!-- Correct -->
<form action="upload" enctype="multipart/form-data" method="POST">
File Data Empty
Symptom: eFile fires but FileSize is 0
Causes:
- No file was selected in the browser
- File read error occurred
- File descriptor was closed prematurely
Solution: Check that a file is selected before submitting
Checkbox Value Not Received
Symptom: Checkbox field not seen in eVariable events
Note: This is normal browser behavior - unchecked checkboxes are NOT sent in the form data
Solution: Initialize checkbox variables to false in eStartingPost, then set to true only when the variable is received
case eStartingPost:
showBinary = false;
break;
case eVariable:
if (strcmp("show_binary", pName) == 0)
{
showBinary = true;
}
break;
Response Not Displayed
Symptom: Browser shows error or blank page after upload
Causes:
- No response sent in eEndOfPost
- Missing HTTP header
- Response sent in wrong event
Solution: Always send response in eEndOfPost:
Large File Truncation
Symptom: Only part of large file is received
Cause: File exceeds FILE_BUFFER_SIZE
Solution: Increase FILE_BUFFER_SIZE in formcode.cpp or inform user of the limit. Current limit: 500KB
Security Considerations
- HTML special characters are encoded to prevent XSS attacks
- File size is limited to prevent memory exhaustion
- No persistent storage - uploaded data is temporary
- Implement authentication for production deployments
- Consider validating file types if needed
Dependencies
Required NetBurner headers:
+----------------+----------------------------------------+
| Header | Purpose |
+----------------+----------------------------------------+
| <
init.h> | System initialization |
| <nbrtos.h> | RTOS functions (
OSTimeDly, etc.) |
| <http.h> | HTTP server functions |
| <httppost.h> | POST handling, FilePostStruct |
| <
fdprintf.h> | Socket printf functions |
| <string.h> | String manipulation |
+----------------+----------------------------------------+
void OSTimeDly(uint32_t to_count)
Delay the task until the specified value of the system timer ticks. The number of system ticks per se...
Definition nbrtos.h:1855
void init()
System initialization. Ideally called at the beginning of all applications, since the easiest Recover...
API Reference
PostEvents Enumeration
+----------------+------------------------------------------------+
| Event | Description |
+----------------+------------------------------------------------+
| eStartingPost | Beginning of POST, initialize state |
| eVariable | Form field received (name/value pair) |
| eFile | File upload field (pValue = FilePostStruct*) |
| eEndOfPost | All data received, send response now |
+----------------+------------------------------------------------+
FilePostStruct Members
+----------------+---------------+--------------------------------+
| Member | Type | Description |
+----------------+---------------+--------------------------------+
| fd | int | File descriptor for reading |
| pFileName | const char* | Original filename from browser |
+----------------+---------------+--------------------------------+
Key Functions
+----------------------------------+----------------------------------+
| Function | Description |
+----------------------------------+----------------------------------+
|
dataavail(fd) | Check
if data available on fd |
|
read(fd, buf, len) | Read bytes from file descriptor |
|
close(fd) | Close file descriptor |
+----------------------------------+----------------------------------+
int write(int fd, const char *buf, int nbytes)
Write data to the stream associated with a file descriptor (fd). Can be used to write data to stdio,...
void RedirectResponse(int sock, PCSTR new_page)
Redirect a HTTP request to a different page.
Related Examples
- HtmlFormPost - URL-encoded form handling (simpler, no files)
- FilePost - Basic file upload example
- FilePost_Handoff - File upload with file system storage