flaudre
flaudre

Reputation: 2298

How to upload a variable amount of files using a single multipartform post header key

I have a repeatable form field:

<div class="repeat">
    <input type="file" name="files[==row-count-placeholder==]" />
</div>

that will (using jQuery) for example result in

<div class="repeat">
    <input type="file" name="files[0]" />        
    <input type="file" name="files[1]" />  
    <input type="file" name="files[2]" />
    <!-- and so on -->
</div>

according to how many files want to be uploaded by the user. The forms method is postand enctype is multipart/form-data.

Using cherrypy as my server and voluptuous validation library I would like to save the uploaded files:

import voluptuous

def save_uploaded_file(file, path)
    #save file on server...

def validate_files(files):    
    for file in files:
        save_uploaded_file(file, cherrypy.request.config['static_dir'])

@cherrypy.expose
def index(self, **kwargs):

    schema = Schema({
        'files' : validate_files
    }, required = True, extra = True)

    kwargs = schema(kwargs)

Therefore I would actually need a post header that contains information for all the files (best would be something like a list of the files) according to one key called files, but all I get is multiple keys such as files[0], files[1] and so on...

How do I approach this? Do I somehow have to manually create an array that contains all the files information or is there a more common or practical way to do it?

Solution (following saaj's suggestion):

schema_dict = {
    'name' : All(Length(min=3, msg="Can't believe that there is a name less than 3 characters...")),
    # ...
    }

# validate files
isPart = lambda v: isinstance(v, cherrypy._cpreqbody.Part)      
files1 = [a for a in kwargs.values() if isPart(a)]
files2 = [a for a in cherrypy.request.params.values() if isPart(a)]
assert files1 == files2

for file in files1:
    # for each file add dict entry and route to validation function
    schema_dict.update({file.name : validate_file}) 

schema = volu.Schema(schema_dict, required = True, extra = True)

Like this a Schema can obviously contain many other fields. Submitted files are being added generically to any Schema. Cool!

Upvotes: 3

Views: 378

Answers (1)

saaj
saaj

Reputation: 25234

Just take file parts from request (if your form doesn't contain other types of parts you can take request's params as is).

@cherrypy.expose
def index(self, **kwargs):
  isPart = lambda v: isinstance(v, cherrypy._cpreqbody.Part)
  files1 = [a for a in kwargs.values() if isPart(a)]
  files2 = [a for a in cherrypy.request.params.values() if isPart(a)]

  assert files1 == files2
  print(files1) # pass it where you want to

Related CherryPy questions: upload performance, asynchronous upload.

Upvotes: 1

Related Questions