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,
                              requires=[IS_NOT_EMPTY(),
                                        IS_LENGTH(1048576, 1024, error_message=CustomMessages.UPLOAD_CV_SIZE)
                                       ]
                              ),
                        INPUT(_type='submit'))
            
                if form.accepts(request.vars, formname=formname):
                    stream=eval('form.vars.%s.file'%formname)
                    filename= eval('form.vars.%s.filename'%formname)        
                    file_id = db.cvs.insert(cv=db.cvs.cv.store(stream,filename=filename),
                                        job_title=job.title,
                                        job_id=job.id)
                
                    # 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:

    stream=form.vars.field_name.file
    filename= form.vars.field_name.filename
    

    Now you can store your file name into the database

    db.cvs.insert(cv=db.cvs.cv.store(stream,filename=filename),
                                        job_title=job.title,
                                        job_id=job.id)
    
    

    see the :

    db.table_name.field_of_type_upload.store()
    
  • 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
        s=cStringIO.StringIO() 
        
        (filename,file) = db.cvs.cv.retrieve(db.cvs[file_id].cv)
        s.write(file.read())  
        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,\
                                                   c='files',
                                                   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():
          filename=request.args[0]
          path=os.path.join(request.folder,'uploads',filename)
          response.headers['ContentType'] ="application/octet-stream";
          response.headers['Content-Disposition']="attachment; filename="  
    +filename
          return response.stream(open(filename),chunk_size=4096)
    
    Advertisements

    IS_IN_SET with or without combo box !!

    April 20, 2010

    Look at this piece of code :

    CURRENCY_SET = [
                       T('EGP'),
                       T('EUR'),
                       T('USD')
    
                    ]
    db.define_table('service',
                    Field('name', 'string', label=T('Name')),
                    Field('description', 'string', label=T('Description')),
                    Field('price', 'double', label=T('Price')),
                    Field('currency', 'string', required=True,
                          requires=[
                                    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