File Upload including Drag and Drop Upload

In this tutorial we will show you how to create a traditional upload form and then move on to creating a drag & drop uploader. All new browsers such as IE10, Firefox, Chrome, Safari, and Opera support the new HTML5 drag and drop upload standard. Drag and drop upload makes it much easier for the user to upload data. It’s also very easy to implement a progress bar for drag and drop upload.

Traditional upload form:

You must first build an HTML form that lets users select a file to upload before you can use server side LSP to manage your uploads. See our HTML Forms for Beginners tutorial if you are new to HTML forms. The following HTML form contains all HTML elements required for a file upload form.

<form enctype="multipart/form-data" method="post">
Choose a file to upload: <input name="thefile" type="file" />
<br/>
<input type="submit" value="Upload File" />
</form>

Example 1: HTML form that lets users select a file to upload.

  • The enctype attribute of the form tag specifies which content-type to use when submitting the form; "multipart/form-data" is used for file uploads.
  • The type="file" attribute of the input tag specifies that the input should be processed as a file.
  • The action tag points to the resource in the server that manages the upload. This has traditionally been sent to a CGI script such as action="/cgi-bin/myupload". However, LSP makes it possible to manage the upload in the same page as the HTML form; thus the action tag is therefore not needed. The browser will send the form data to the same resource in the server when this tag is not present.

After the user clicks submit, the data will be posted (sent back) to the same LSP page that presented the upload form. The following LSP code is all that is required on the server for receiving and saving the received data to a file.

<?lsp
local err
if request:method() == "POST" then
   -- Open a file pointer for the data we will receive.
   local fp
   fp,err=io:open("the-file-name","w")
   if not fp then
      err = "Cannot open file: "..err
   else
      -- filedata: save data chunks received from the client
      local function filedata(data) fp:write(data) end
      request:multipart{
         beginfile=function() end, -- Required, but not used.
         filedata=filedata,
         error=function(e) err=e end
      }
      fp:close() -- Upload complete
      if err then err = "Upload failed: "..err end
   end
end
if err then response:write('<p>',err,'</p>') end
?>

Example 2: LSP example code that shows how to save posted "multipart/form-data" to a file.

The above code executes if the HTTP method is POST. The method request:multipart decodes the multi-part stream received from the browser and calls the callback function ‘filedata’ for each file data chunk that has been decoded. The request:multipart method also requires a "beginfile" function, but we are not using this function in the above example so we have created a dummy function that does nothing. The above code uses the IO interface defined in the web application and the application can for this reason not be deployed, i.e. not be a ZIP file. You can change this code and use any of the Mako Server IO Interfaces. You can also use the standard Lua input/output functions.

Drag and Drop Upload

All modern browsers support the new XMLHttpRequest Level 2 specification which makes it possible to design drag and drop logic in JavaScript.

Drag Over Event:

To visualize that we support drag and drop, we’ll install a “dragover” JavaScript callback. The easiest way to do this is to use JQuery.

var dragover=false
$('body').bind('dragover',function(e) {
    e.preventDefault();
    if(dragover) return;
    dragover=true;
    $('#dropbox').fadeTo(300,.3,function(){
        $('#dropbox').fadeTo(300,1, function() {dragover=false;});});
});

Example 3: Shows how to install a dragover callback using JQuery.

The ‘dragover’ callback function, which we bind to the ‘body’ element, triggers when the user drags a file over the browser window. The ‘dropbox’ element, which we reference on the two last lines in the above code, is for a div element with the drop image to the right. The chained fadeTo functions make the drop box oscillate. You can test this by dragging a file over the image to the right. The image to the right should then start to oscillate by fading in and out. Note, do not drop the file since this page is not designed for accepting ‘drop’ events.

Drop box

The ‘dragover’ callback is not required, but it provides a visual clue to the user that we support drag and drop. You can make the ‘dragover’ callback perform any type of visual clue, and you do no have to use the code we provided above.

Drop Event:

The browser fires a ‘drop’ event when a user drops a file into a browser window. We must install a callback that catches this event and prevents the default browser action.

// The drop event callback i.e. drag and drop
function drop(e) {
     e.preventDefault(); //Prevent the default browser action
    // We accept one file. Additional files are ignored.
    var file=e.originalEvent.dataTransfer.files[0];
    var xhr = new XMLHttpRequest(); //Create the upload object
    //Bind a "progress" callback
    xhr.upload.addEventListener("progress", my_progress);
    //Open connection to origin i.e. to LSP page
    xhr.open("PUT", window.location.href);
    xhr.send(file); // Start the upload
};

$('body').bind('drop',drop); // Bind the drop function to the drop event

Example 4: Shows how to install a drop event callback function.

The above code is designed such that it accepts one dropped file. You can redesign the code and make it accept all files dropped into the browser window. As an example, the Web File Manager used in the How to Create a Cloud Server tutorial accepts all files dropped into the browser window.

The above code creates a XMLHttpRequest object and binds a “progress” event to the object. The code for this callback is not shown in the above code snippet. The complete JavaScript code for the above is provided in the example code presented at the end of this tutorial.

Notice that we use the HTTP PUT method in the above example and not HTTP POST when sending data to the server. The HTTP PUT method was designed specifically for uploading files and the Mako server provides an object that manages HTTP PUT uploads.

<?lsp
local err
if request:method() == "PUT" then
   -- Open a file pointer for the data we will receive.
   local fp
   fp,err=io:open("the-file-name","w")
   if not fp then
      err = "Cannot open file:"..err
   else
      -- filedata: save data chunks received from the client
      local function filedata(data) fp:write(data) end
      for data in request:rawrdr() do filedata(data) end
      fp:close() -- Upload complete
   end
end
?>

Example 5: Shows how to use request:rawrdr() for receiving HTTP PUT data.

The code in example 5 is similar to the code in example 2. The request:rawrdr() method returns a Lua iterator that makes it possible to iterate over the received HTTP PUT data. The data received is not buffered inside the request:rawrdr() method, but is instead returned as chunks as long as data trickles in.

You can easily combine the code in example 2 and 5. The example code, which you can download below, combines the code in example 2 and 5.  This code automatically detects if the data received should be parsed by request:multipart (example 2) or managed by the iterator returned by method request:rawrdr (example 5).

Download Example Code

The example code we provide includes all of the above, but the code is designed such that it only accepts the uploading of ZIP files. The uploaded ZIP file is then mounted by using the ZipIo. The purpose with the example is to show engineers how to use a browser for firmware upgrades; however, most of the example code is related to the topics covered above.

Download example from GitHub

Limitations in the above example (blocking v.s. non blocking sockets)

The above example code uses the server’s blocking API functions. This means that each active upload requires an executing thread. The Mako Server can be configured to use a large number of threads, but you will eventually run into limitations if you plan on supporting many concurrent uploads.

The Mako Server also provides a more advanced upload API that uses non blocking sockets. This API should be used if you have requirements where you need to support many concurrent uploads. The Web File Manager, which is introduced in the How to Create a Cloud Server tutorial, uses this API. We also provide a non blocking firmware upload example, which is identical to the code above, except for that the code uses the non blocking API.


Discover More:

Whether you are a maker, a startup, or a large business, we've got you covered. Please send us an email if you have any questions or if you are unsure on what product to select. We are here to help you find the best solution, and we'd really like to help you with your hardware/software project challenges.


OPC-UA

OPC-UA Client & Server

An easy to use OPC UA stack that enables bridging of OPC-UA enabled industrial products with cloud services, IT, and HTML5 user interfaces.

Edge Controller

Edge Controller

Use our user programmable Edge-Controller as a tool to accelerate development of the next generation industrial edge products and to facilitate rapid IoT and IIoT development.

On-Premises IoT

On-Premises IoT Platform

Learn how to use the Barracuda App Server as your On-Premises IoT Foundation.

Embedded Web Server

Barracuda Embedded Web Server

The compact Web Server C library is included in the Barracuda App Server protocol suite but can also be used standalone.

WebSocket Server

Microcontroller Friendly

The tiny Minnow Server enables modern web server user interfaces to be used as the graphical front end for tiny microcontrollers. Make sure to check out the reference design and the Minnow Server design guide.

WebDAV Server

Network File System

Why use FTP when you can use your device as a secure network drive.

HTTP Client

Secure HTTP Client Library

PikeHTTP is a compact and secure HTTP client C library that greatly simplifies the design of HTTP/REST style apps in C or C++.

WebSocket Client

Microcontroller Friendly

The embedded WebSocket C library lets developers design tiny and secure IoT applications based on the WebSocket protocol.

SMTP Client

Secure Embedded SMTP Library

Send alarms and other notifications from any microcontroller powered product.

Crypto Library

RayCrypto C Library

The RayCrypto engine is an extremely small and fast embedded crypto library designed specifically for embedded resource-constrained devices.

Embedded PKI Service

Automatic SSL Certificate Management for Devices

Real Time Logic's SharkTrust™ service is an automatic Public Key Infrastructure (PKI) solution for products containing an Embedded Web Server.

Modbus

Modbus TCP client

The Modbus client enables bridging of Modbus enabled industrial products with modern IoT devices and HTML5 powered HMIs.

Posted in Tutorials by bd