1

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?

stenci
  • 8,290
  • 14
  • 64
  • 104
  • See [cherrypy._cprequest.Request.body](https://cherrypy.readthedocs.org/en/3.3.0/refman/_cprequest.html#cherrypy._cprequest.Request.body). It seems you need to specify content type on JS side to prevent form parsing and then process the `body`. – jwalker Nov 12 '14 at 10:30

1 Answers1

2

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)
saaj
  • 23,253
  • 3
  • 104
  • 105