Upload Download files

April 28, 2010
  • Using multiple tables in the same page as you know requires explicitly adding form name when you accept it
  • suppose a situation in which you need to make multiple upload forms in the same page may be for users to upload their cvs
  • Now you’ve 3 problems :
    • How could you add multiple forms in the same page and in the same time make them work ?
    • How to upload the files (cvs)
    • How to download them
    def jobs():
            jobs = db(...).select(...)
            for job in jobs:
                # I'll use the same name for form name as well as the field
                formname = "upload_f_%s"%job.id
                form = FORM(INPUT(_type="file",_name=formname,
                                        IS_LENGTH(1048576, 1024, error_message=CustomMessages.UPLOAD_CV_SIZE)
                if form.accepts(request.vars, formname=formname):
                    filename= eval('form.vars.%s.filename'%formname)        
                    file_id = db.cvs.insert(cv=db.cvs.cv.store(stream,filename=filename),
                    # add permissions for that file    
                    # do other stuff       
  • As you see , I made a form with a field of type ‘file’ and form name is variable based on the job_id , C.Vs are going to be uploaded
    this helps making multiple forms per page and in the same time every form has a unique name that will be used when accepting the form .
  • Now as you may have guessed the form containing a ‘file’ field will have (when being accepted) :
    form.vars.field_name.file  -> get the reference to the file
    form.vars.field_name.filename  -> get the file name

    Now you can easily do something like:

    filename= form.vars.field_name.filename

    Now you can store your file name into the database


    see the :

  • Congratulations !!! you’ve uploaded your files

  • Now for downloading files you can do :
    def download_cv():
        file_id = request.args(0)
        import cStringIO 
        import contenttype as c
        (filename,file) = db.cvs.cv.retrieve(db.cvs[file_id].cv)
        response.headers['Content-Type'] = c.contenttype(filename)
        response.headers['Content-Disposition'] = \
                    "attachment; filename=%s" % filename  
        return s.getvalue()

    Now you can make another function that list all cvs available with a link for downloading them as follows:

    def list_Cvs():
        cvs = db(db.cvs.id>0).select(db.cvs.cv) # db.cvs.cv is of type 'upload'
        urls = [A('Download', _href=URL(r=request,\
                                                   f='download_cv', args=[row.id]
                     )) for row in cvs]
        return urls
  • what is the benefit of writing my own download function ?
    This enables you to write your own authorization logic before allowing a user to download a file

  • Now what if I just don’t want to write my own download function and use web2py facilities for that matter ?

  • You can use response.stream(), or response.download() functions.
  • What is difference between them?

  • In fact response.stream is the main download function that takes care of streaming files.
    response.download() calls response.stream() to do the streaming stuff.
  • response.download() has its arguments as request, db and is used for downloading files by their names, while the name is the name of the file in the database.
                def download():
                    return response.download(request, db)
            #downloads from http://..../download/filename

    response.download() takes request to extract from it the file name stored in [request.args]

    download function is mainly used by SQLFORM to update a record that includes an upload field that is used to upload an image, in this case response.download() gets the file (image) to be displayed when updating this record.

    form = SQLFORM(db.my_table, record, upload=URL(r=request, c='default', f='download'))
  • Suppose you want to stream an MP3 file, you can do something like:

    Somebody asked how to stream an mp3 file from the uploads files directly:
    def get_my_file():
          response.headers['ContentType'] ="application/octet-stream";
          response.headers['Content-Disposition']="attachment; filename="  
          return response.stream(open(filename),chunk_size=4096)

    IS_IN_SET with or without combo box !!

    April 20, 2010

    Look at this piece of code :

                    Field('name', 'string', label=T('Name')),
                    Field('description', 'string', label=T('Description')),
                    Field('price', 'double', label=T('Price')),
                    Field('currency', 'string', required=True,
                                    IS_IN_SET(CURRENCY_SET, zero=T('--choose currency--'))
                    Field('image', 'upload', label=T('Photo'))

    You expect it to get you a combo box in your field to choose among different currencies in your system
    but it doesn’t offer you that , it just give you a Normal text field that you should enter in it the currency
    and will not accept values except ‘EGP’, ‘EUR’, or ‘USD’

    So what’s wrong and how can I get the combo box that I want ?

    Simply, when you want IS_IN_SET to get you a combo box , don’t use it like this :

    requires=[IS_IN_SET(CURRENCY_SET, zero=T('--choose currency--'))]

    instead use :

    requires=IS_IN_SET(CURRENCY_SET, zero=T('--choose currency--'))

    Tricky hah ?  😀

    Any way there’s a solution for this, please refer to This post