<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://www.ethernut.de/nutwiki/index.php?action=history&amp;feed=atom&amp;title=Documents%2FuHTTP_Lib_Uploads</id>
		<title>Documents/uHTTP Lib Uploads - Revision history</title>
		<link rel="self" type="application/atom+xml" href="http://www.ethernut.de/nutwiki/index.php?action=history&amp;feed=atom&amp;title=Documents%2FuHTTP_Lib_Uploads"/>
		<link rel="alternate" type="text/html" href="http://www.ethernut.de/nutwiki/index.php?title=Documents/uHTTP_Lib_Uploads&amp;action=history"/>
		<updated>2026-04-28T22:46:40Z</updated>
		<subtitle>Revision history for this page on the wiki</subtitle>
		<generator>MediaWiki 1.26.2</generator>

	<entry>
		<id>http://www.ethernut.de/nutwiki/index.php?title=Documents/uHTTP_Lib_Uploads&amp;diff=367&amp;oldid=prev</id>
		<title>Harald: Created page with &quot;&lt;div id=&quot;content&quot;&gt;  = MicroHTTP Library: Uploading Files =  The capability to upload files can significantly improve the maintainability of your webserver based application. Y...&quot;</title>
		<link rel="alternate" type="text/html" href="http://www.ethernut.de/nutwiki/index.php?title=Documents/uHTTP_Lib_Uploads&amp;diff=367&amp;oldid=prev"/>
				<updated>2017-07-13T08:32:20Z</updated>
		
		<summary type="html">&lt;p&gt;Created page with &amp;quot;&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;  = MicroHTTP Library: Uploading Files =  The capability to upload files can significantly improve the maintainability of your webserver based application. Y...&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;div id=&amp;quot;content&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= MicroHTTP Library: Uploading Files =&lt;br /&gt;
&lt;br /&gt;
The capability to upload files can significantly improve the maintainability of your webserver based application. You may, for example, allow users to upload new firmware versions. Unfortunately, compared to other features of the [[uhttplib.html|MicroHTTP library]], the implementation requires more additional application code.&lt;br /&gt;
&lt;br /&gt;
== Web Content ==&lt;br /&gt;
&lt;br /&gt;
The following HTML code presents a form that allows to replace an image displayed on the same page.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;&amp;amp;lt;html&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;head&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;title&amp;amp;gt;File Upload&amp;amp;lt;/title&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;/head&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;body&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;h1&amp;amp;gt;Upload Sample&amp;amp;lt;/h1&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;p&amp;amp;gt;Upload PNG image&amp;amp;lt;/p&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;form action=&amp;amp;quot;upload.cgi&amp;amp;quot; method=&amp;amp;quot;post&amp;amp;quot; enctype=&amp;amp;quot;multipart/form-data&amp;amp;quot;&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;table cellspacing=&amp;amp;quot;10&amp;amp;quot; width=&amp;amp;quot;100%&amp;amp;quot;&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;tr&amp;amp;gt;&amp;amp;lt;td&amp;amp;gt;&amp;amp;lt;input type=&amp;amp;quot;file&amp;amp;quot; size=&amp;amp;quot;64&amp;amp;quot; name=&amp;amp;quot;image&amp;amp;quot;&amp;amp;gt;&amp;amp;lt;/td&amp;amp;gt;&amp;amp;lt;/tr&amp;amp;gt;&lt;br /&gt;
    &amp;amp;lt;tr&amp;amp;gt;&amp;amp;lt;td&amp;amp;gt;&amp;amp;lt;input type=&amp;amp;quot;submit&amp;amp;quot; name=&amp;amp;quot;upload&amp;amp;quot; value=&amp;amp;quot;Send&amp;amp;quot; onClick=&amp;amp;quot;return confirm('Are you sure?');&amp;amp;quot;&amp;amp;gt;&amp;amp;lt;/td&amp;amp;gt;&amp;amp;lt;/tr&amp;amp;gt;&lt;br /&gt;
  &amp;amp;lt;/table&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;/form&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;img src=&amp;amp;quot;image.png&amp;amp;quot; /&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;/body&amp;amp;gt;&lt;br /&gt;
&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
When clicking the ''Send'' button, the browser will query the user for confirmation (see ''onClick'') and, if confirmed, transfer the data with the POST method to the CGI script ''upload.cgi''.&lt;br /&gt;
&lt;br /&gt;
== Application Code ==&lt;br /&gt;
&lt;br /&gt;
The application must process the form data with a CGI function, registered under the name ''upload.cgi''. The required code to start the webserver is similar to the one we used when explaining the [[uhttplib_forms.html|common gateway interface]] in general.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;StreamInit();&lt;br /&gt;
MediaTypeInitDefaults();&lt;br /&gt;
HttpRegisterCgiFunction(&amp;amp;quot;upload.cgi&amp;amp;quot;, CgiUpload);&lt;br /&gt;
HttpRegisterMediaType(&amp;amp;quot;cgi&amp;amp;quot;, NULL, NULL, HttpCgiFunctionHandler);&lt;br /&gt;
StreamClientAccept(HttpdClientHandler, NULL);&amp;lt;/pre&amp;gt;&lt;br /&gt;
The CGI function is kept simple:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;#define HTTP_ROOT   (http_root ? http_root : HTTP_DEFAULT_ROOT)&lt;br /&gt;
extern char *http_root;&lt;br /&gt;
&lt;br /&gt;
int CgiUpload(HTTPD_SESSION *hs)&lt;br /&gt;
{&lt;br /&gt;
    char *upname;&lt;br /&gt;
    char *lclname;&lt;br /&gt;
&lt;br /&gt;
    lclname = malloc(strlen(HTTP_ROOT) + sizeof(&amp;amp;quot;image.png&amp;amp;quot;));&lt;br /&gt;
    strcat(strcpy(lclname, HTTP_ROOT), &amp;amp;quot;image.png&amp;amp;quot;);&lt;br /&gt;
    upname = UploadFile(hs, lclname);&lt;br /&gt;
    HttpSendRedirection(hs, 303, &amp;amp;quot;/index.html&amp;amp;quot;, NULL);&lt;br /&gt;
&lt;br /&gt;
    return 0;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
It calls ''UploadFile'' to do all the work. And this is where things become a bit more complicated. When using a form with an input field of type ''file'', we must also declare the encoding type of the form as ''multipart/form-data''. This enables the browser to send both, the data entered by the user as well as the contents of the given file.&lt;br /&gt;
&lt;br /&gt;
Multipart form data is, as its name implies, sent in parts, which are separated by a boundary string. The real trouble is, that the browser only informs the server about the overall content length. But the server is completely unaware about the size of each part. As long as text lines are transferred, it's not too hard to detect the next boundary string that marks the end of each part. When, like in our case with images, binary data is transfered, detecting the next boundary without significantly degrading the transfer speed can become tricky.&lt;br /&gt;
&lt;br /&gt;
I will first present the full code of the upload function before turning into a few details.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;#define MAX_UPSIZE  1460&lt;br /&gt;
#define MIN(a, b) ((a) &amp;amp;lt; (b) ? (a) : (b))&lt;br /&gt;
&lt;br /&gt;
char *UploadFile(HTTPD_SESSION *hs, char *path)&lt;br /&gt;
{&lt;br /&gt;
    char *rp = NULL;&lt;br /&gt;
    char *upname = NULL;&lt;br /&gt;
    long avail;&lt;br /&gt;
    char *line;&lt;br /&gt;
    char *delim;&lt;br /&gt;
    const char *sub_ptr;&lt;br /&gt;
    int sub_len;&lt;br /&gt;
    int fd = -1;&lt;br /&gt;
    int got = 0;&lt;br /&gt;
    HTTP_STREAM *stream = hs-&amp;amp;gt;s_stream;&lt;br /&gt;
    HTTP_REQUEST *req = &amp;amp;amp;hs-&amp;amp;gt;s_req;&lt;br /&gt;
&lt;br /&gt;
    /* Retrieve the boundary string. */&lt;br /&gt;
    delim = GetMultipartBoundary(req);&lt;br /&gt;
    if (delim == NULL) {&lt;br /&gt;
        return NULL;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    avail = req-&amp;amp;gt;req_length;&lt;br /&gt;
    line = malloc(MIN(avail, MAX_UPSIZE) + 1);&lt;br /&gt;
    if (line == NULL) {&lt;br /&gt;
        /* No memory. */&lt;br /&gt;
        free(delim);&lt;br /&gt;
        return NULL;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    /* If we have a delimiter, then process the boundary content. */&lt;br /&gt;
    while (avail &amp;amp;gt; 0) {&lt;br /&gt;
        /* Parse the next boundary header. */&lt;br /&gt;
        if (HttpParseMultipartHeader(hs, delim, &amp;amp;amp;avail)) {&lt;br /&gt;
            /* Broken connection. */&lt;br /&gt;
            break;&lt;br /&gt;
        }&lt;br /&gt;
        /* Ignore headers without content disposition line. */&lt;br /&gt;
        if (req-&amp;amp;gt;req_bnd_dispo) {&lt;br /&gt;
            /* Retrieve the name of the form item. */&lt;br /&gt;
            sub_ptr = HttpArgValueSub(req-&amp;amp;gt;req_bnd_dispo, &amp;amp;quot;name&amp;amp;quot;, &amp;amp;amp;sub_len);&lt;br /&gt;
            if (sub_ptr) {&lt;br /&gt;
                /* The item named 'image' contains the binary data of the file. */&lt;br /&gt;
                if (strncasecmp(sub_ptr, &amp;amp;quot;image&amp;amp;quot;, sub_len) == 0) {&lt;br /&gt;
                    char *filename = NULL;&lt;br /&gt;
                    int fd = -1;&lt;br /&gt;
                    int eol = 0;&lt;br /&gt;
&lt;br /&gt;
                    /* Get the upload file name. */&lt;br /&gt;
                    sub_ptr = HttpArgValueSub(req-&amp;amp;gt;req_bnd_dispo, &amp;amp;quot;filename&amp;amp;quot;, &amp;amp;amp;sub_len);&lt;br /&gt;
                    if (sub_ptr &amp;amp;amp;&amp;amp;amp; sub_len) {&lt;br /&gt;
                        upname = malloc(sub_len + 1);&lt;br /&gt;
                        if (upname) {&lt;br /&gt;
                            memcpy(upname, sub_ptr, sub_len);&lt;br /&gt;
                            upname[sub_len] = 0;&lt;br /&gt;
                            /* Open the local file that the caller has provided. */&lt;br /&gt;
#ifdef NUT_OS&lt;br /&gt;
                            fd = _open(path, _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY);&lt;br /&gt;
#else&lt;br /&gt;
                            fd = _open(path, _O_CREAT | _O_TRUNC | _O_RDWR | _O_BINARY, _S_IREAD | _S_IWRITE);&lt;br /&gt;
#endif&lt;br /&gt;
                            if (fd == -1) {&lt;br /&gt;
                                printf(&amp;amp;quot;Error %d opening %s\n&amp;amp;quot;, errno, path);&lt;br /&gt;
                            } else {&lt;br /&gt;
                                printf(&amp;amp;quot;Uploading %s\n&amp;amp;quot;, upname);&lt;br /&gt;
                            }&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    /* Recieve the binary data. */&lt;br /&gt;
                    while (avail) {&lt;br /&gt;
                        /* Read until the next boundary line. */&lt;br /&gt;
                        got = StreamReadUntilString(stream, delim, line, MIN(avail, MAX_UPSIZE));&lt;br /&gt;
                        if (got &amp;amp;lt;= 0) {&lt;br /&gt;
                            break;&lt;br /&gt;
                        }&lt;br /&gt;
                        avail -= got;&lt;br /&gt;
                        /* Write data to the local file, if one had been opened. */&lt;br /&gt;
                        if (fd != -1) {&lt;br /&gt;
                            if (eol) {&lt;br /&gt;
                                _write(fd, &amp;amp;quot;\r\n&amp;amp;quot;, 2);&lt;br /&gt;
                            }&lt;br /&gt;
                            if (got &amp;amp;gt;= 2 &amp;amp;amp;&amp;amp;amp; line[got - 2] == '\r' &amp;amp;amp;&amp;amp;amp; line[got - 1] == '\n') {&lt;br /&gt;
                                eol = 1;&lt;br /&gt;
                                got -= 2;&lt;br /&gt;
                            }&lt;br /&gt;
                            _write(fd, line, got);&lt;br /&gt;
                        }&lt;br /&gt;
                    }&lt;br /&gt;
                    if (fd != -1) {&lt;br /&gt;
                        _close(fd);&lt;br /&gt;
                    }&lt;br /&gt;
                    free(filename);&lt;br /&gt;
                    if (got &amp;amp;lt; 0) {&lt;br /&gt;
                        /* Broken connection. */&lt;br /&gt;
                        break;&lt;br /&gt;
                    }&lt;br /&gt;
                    rp = upname;&lt;br /&gt;
                }&lt;br /&gt;
                else if (strncasecmp(sub_ptr, &amp;amp;quot;upload&amp;amp;quot;, sub_len) == 0) {&lt;br /&gt;
                    got = StreamReadUntilChars(hs-&amp;amp;gt;s_stream, &amp;amp;quot;\n&amp;amp;quot;, &amp;amp;quot;\r&amp;amp;quot;, line, MIN(avail, MAX_UPSIZE));&lt;br /&gt;
                    if (got &amp;amp;lt;= 0) {&lt;br /&gt;
                        break;&lt;br /&gt;
                    }&lt;br /&gt;
                }&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    if (fd != -1) {&lt;br /&gt;
        _close(fd);&lt;br /&gt;
    }&lt;br /&gt;
    free(delim);&lt;br /&gt;
    free(line);&lt;br /&gt;
&lt;br /&gt;
    return rp;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note, that this function will directly read the data stream that is transmitted by the webbrowser. First, it will call ''GetMultipartBoundary'' to retrieve the boundary string that delimits each mime part. It will then allocate a read buffer, which size is limited to either the total content length or a maximum defined by MAX_UPSIZE, whichever is smaller. This prevents the application to run out of memory on tiny embedded systems.&lt;br /&gt;
&lt;br /&gt;
After the buffer has been successfully allocated, the CPU will execute a loop until all data from the browser will have been consumed. At its beginning the function ''HttpParseMultipartHeader'' is called to parse the mime header. This routine adds the result to the HTTP_REQUEST structure. Most notably, the mime header ''Content-Disposition'' is stored in a buffer, to which ''req-&amp;amp;gt;req_bnd_dispo'' points to. The application can request specific items from this header by calling the library function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;const char *HttpArgValueSub(const char *str, const char *name, int *len);&amp;lt;/pre&amp;gt;&lt;br /&gt;
If an item named ''image'' appears, ''HttpArgValueSub()'' is called to retrieve the filename. To receive the binary data (uploaded image file), the function&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;int StreamReadUntilString(HTTP_STREAM *sp, const char *delim, char *buf, int siz);&amp;lt;/pre&amp;gt;&lt;br /&gt;
is called, which delivers data up to the next multipart boundary string. If reached, the loop is repeated, looking for the next mime header.&lt;br /&gt;
&lt;br /&gt;
I mentioned above, that ''GetMultipartBoundary'' is called to determine the boundary string. This routine must be provided by the application code too.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;char *GetMultipartBoundary(HTTP_REQUEST *req)&lt;br /&gt;
{&lt;br /&gt;
    char *rp = NULL;&lt;br /&gt;
    const char *bptr;&lt;br /&gt;
    int blen;&lt;br /&gt;
&lt;br /&gt;
    /* Make sure this is a multipart post. */&lt;br /&gt;
    if (CheckForPost(req, 1) == 0) {&lt;br /&gt;
        /* Retrieve the boundary string. */&lt;br /&gt;
        bptr = HttpArgValueSub(req-&amp;amp;gt;req_type, &amp;amp;quot;boundary&amp;amp;quot;, &amp;amp;amp;blen);&lt;br /&gt;
        if (bptr) {&lt;br /&gt;
            /* Build a delimiter string. */&lt;br /&gt;
            rp = malloc(blen + 3);&lt;br /&gt;
            if (rp) {&lt;br /&gt;
                rp[0] = '-';&lt;br /&gt;
                rp[1] = '-';&lt;br /&gt;
                memcpy(rp + 2, bptr, blen);&lt;br /&gt;
                rp[blen + 2] = '\0';&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    return rp;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
Before reading any data, this function calls ''CheckForPost'' to make sure, that POST data is indeed available.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre class=&amp;quot;coding&amp;quot;&amp;gt;int CheckForPost(HTTP_REQUEST * req, int type)&lt;br /&gt;
{&lt;br /&gt;
    if (req-&amp;amp;gt;req_method != HTTP_METHOD_POST || req-&amp;amp;gt;req_type == NULL) {&lt;br /&gt;
        /* Bad method, POST expected. */&lt;br /&gt;
        return -1;&lt;br /&gt;
    }&lt;br /&gt;
    if (type == 0 &amp;amp;amp;&amp;amp;amp; strncasecmp(req-&amp;amp;gt;req_type, &amp;amp;quot;application/x-www-form-urlencoded&amp;amp;quot;, 33) == 0) {&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
    if (strncasecmp(req-&amp;amp;gt;req_type, &amp;amp;quot;multipart/form-data&amp;amp;quot;, 19) == 0) {&lt;br /&gt;
        return 0;&lt;br /&gt;
    }&lt;br /&gt;
    /* Bad content. */&lt;br /&gt;
    return -1;&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Next Step ==&lt;br /&gt;
&lt;br /&gt;
So far we used CGI functions to deliver content to the webbrowser for immediate display. Another technique is to [[uhttplib_ajax.html|asynchronously send data to Javascript]].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;/div&gt;</summary>
		<author><name>Harald</name></author>	</entry>

	</feed>