Uploading a file in ajax to CherryPy

1.4k Views Asked by At

I am trying to upload many files at once to my CherryPy server.

I am following this tutorial that shows PHP code on the server side.

The JavaScript part is simple. Here is a summary of what it does:

function FileSelectHandler(e) {
  var files = e.target.files || e.dataTransfer.files;
  for (var i = 0, f; f = files[i]; i++) {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", "upload", true);
    xhr.setRequestHeader("X_FILENAME", file.name);
    xhr.send(file);
}

I translated the upload.php described in the tutorial into something like this:

def upload(self):
    [...]

When the server receives the request I can see that cherrypy.request.headers['Content-Length'] == 5676 which is the length of the file I'm trying to upload, so I assume the whole file has been sent to the server.

How do I get the content of the file?

1

There are 1 best solutions below

0
On BEST ANSWER

At its minimum it looks like the following. Tested in Firefox and Chromium. If you need to support legacy browsers I'd look at some JavaScript library for polyfills and fallback.

#!/usr/bin/env python
# -*- coding: utf-8 -*-


import os
import shutil

import cherrypy


config = {
  'global' : {
    'server.socket_host' : '127.0.0.1',
    'server.socket_port' : 8080,
    'server.thread_pool' : 8,
  }
}


class App:

  @cherrypy.expose
  def index(self):
    return '''<!DOCTYPE html>
      <html>
      <head>
        <title>CherryPy Async Upload</title>
      </head>
      <body>
        <form id='upload' action=''>
          <label for='fileselect'>Files to upload:</label>
          <input type='file' id='fileselect' multiple='multiple' />
        </form>
        <script type='text/javascript'>
          function upload(file)
          {
            var xhr = new XMLHttpRequest();

            xhr.upload.addEventListener('progress', function(event) 
            {
              console.log('progess', file.name, event.loaded, event.total);
            });
            xhr.addEventListener('readystatechange', function(event) 
            {
              console.log(
                'ready state', 
                file.name, 
                xhr.readyState, 
                xhr.readyState == 4 && xhr.status
              );
            });

            xhr.open('POST', '/upload', true);
            xhr.setRequestHeader('X-Filename', file.name);

            console.log('sending', file.name, file);
            xhr.send(file);
          }

          var select = document.getElementById('fileselect');
          var form   = document.getElementById('upload')
          select.addEventListener('change', function(event)
          {
            for(var i = 0; i < event.target.files.length; i += 1)
            {
              upload(event.target.files[i]); 
            }
            form.reset();
          });
        </script>
      </body>
      </html>
   '''

  @cherrypy.expose
  def upload(self):
    '''Handle non-multipart upload'''

    filename    = os.path.basename(cherrypy.request.headers['x-filename'])
    destination = os.path.join('/home/user', filename)
    with open(destination, 'wb') as f:
      shutil.copyfileobj(cherrypy.request.body, f)


if __name__ == '__main__':
  cherrypy.quickstart(App(), '/', config)