Uploading multiple files to the tornado webserver

The following html code can be used to create an html form that allows uploading multiple files at once:

<form enctype="multipart/form-data" method="POST" action="upload.py">
  <table style="width: 100%">
    <tr>
      <td>Choose the files to upload:</td>
      <td style="text-align: right"><input type="file" multiple="" id="files" name="files"></td>
    </tr>
    <tr>
      <td><input id="fileUploadButton" type="submit" value="Upload &gt;&gt;"></td>
      <td></td>
    </tr>
  </table>
</form>

Using the tornado webserver it is then possible to handle the incoming requests containing an unknown number of files with something like this:

class UploadFile(tornado.web.RequestHandler):
  # handle a post request
  def post(self):
    # ... maybe add a check that checks whether the user is allowed to upload anything ...
    # the file(s) that should get uploaded
    files = []
    # check whether the request contains files that should get uploaded
    try:
      files = self.request.files['files']
    except:
      pass
    # for each file that should get uploaded
    for xfile in files:
      # get the default file name
      file = xfile['filename']
      # the filename should not contain any "evil" special characters
      # basically "evil" characters are all characters that allows you to break out from the upload directory
      index = file.rfind(".")
      filename = file[:index].replace(".", "") + str(time.time()).replace(".", "") + file[index:]
      filename = filename.replace("/", "")
      # save the file in the upload folder
      with open("uploads/%s" % (filename), "w") as out:
        # Be aware, that the user may have uploaded something evil like an executable script ...
        # so it is a good idea to check the file content (xfile['body']) before saving the file
        out.write(xfile['body'])

Keep in mind, that the tornado webserver is storing the complete html request in RAM – which may be to much for a webserver, if multiple users are simultanously uploading big files. So you may need to limit the maximal upload size. If you are using the nginx webserver in combination with tornado, you can limit the maximal upload size in the nginx config file:

# ...
http {
    # ...
    client_max_body_size 100m; # a value of 0 disables max_body_size checking ...
    # ...
}