Download web2py with original file name

I want to upload a file using SQL.factory () I just wanted to keep the original file name my code is currently

form = SQLFORM.factory( Field('file_name', requires=IS_NOT_EMPTY()), Field('file', 'upload',uploadfolder=upload_folder)) if form.accepts(request.vars, session): #.process().accepted: response.flash = u'File uploaded' session.your_name = form.vars.file_name session.filename = request.vars.file elif form.errors: response.flash = 'form has errors' return dict(form=form) 

I think session.filename = request.vars.file is where you set the file name. Why am I getting the auto-generated file name no_data.smth.23u8o8274823zu4i2.smth

thanks

+7
source share
3 answers

First, request.vars.file is a Python object cgi.FieldStorage , so session.filename = request.vars.file should lead to an error. request.vars.file.file is the actual file object, and request.vars.file.filename is the original name of the downloaded file.

When you upload a file through the upload field, web2py automatically generates a new form name 'table_name.field_name.random_id.b16encoded_original_filename.extension'. This is done to prevent directory traversal attacks and enable the loading mechanism (which must know the name of the table and field). In the case of SQLFORM.factory, the database table name is missing, so the default table name is "no_table".

The code you showed should not generate a file name, for example "no_data.smth.23u8o8274823zu4i2.smth". This file name means that you explicitly told SQLFORM.factory use the table name "no_data" (through your table_name argument) and that the name of the load field is "smth". (In the code above, it will generate a file name starting with "no_table.file".)

Note. web2py automatically gets the original name of the downloaded file and encodes it (using b16encode) into a new file name (then it decodes the original file name using the built-in download mechanism). The original file name is also available in form.vars.file.filename. Thus, you do not need the user to enter a file name at all. However, if you want the user to enter a file name that may differ from the actual file name, and then use the file name entered by the user, you can add the following before creating the form:

 if 'file' in request.vars and request.vars.file_name: request.vars.file.filename = request.vars.file_name 

This will reassign the file name of the downloaded file to the value entered by the user, and web2py then encodes the user entered file name into the new file name. Note, however, that web2py relies on a file name extension to properly configure HTTP headers at boot, so you can add some logic to get the original file name extension if the user cannot enter it.

+6
source

If you simply rename the file, this will disrupt the loading mechanism. In addition, sometimes you can save a file under a different name than the original. Suppose you have the following model:

 db.define_table("files", Field("name", unique=True), Field("file", "upload")) 

You need to expand the download field with custom storage and retrieval features:

 Field("file", "upload", custom_store=store_file, custom_retrieve=retrieve_file) 

Functions just write / read a file from a fixed download directory:

 import os import shutil def store_file(file, filename=None, path=None): path = "applications/app_name/uploads" if not os.path.exists(path): os.makedirs(path) pathfilename = os.path.join(path, filename) dest_file = open(pathfilename, 'wb') try: shutil.copyfileobj(file, dest_file) finally: dest_file.close() return filename def retrieve_file(filename, path=None): path = "applications/app_name/uploads" return (filename, open(os.path.join(path, filename), 'rb')) 

Now in the controller you need to change form.vars before inserting / updating the database and set the file name. If you want to keep the original name of the downloaded file, this is optional.

 def validate(form): # set the uploaded file name equal to a name given in the form if form.vars.file is not None: form.vars.file.filename = form.vars.name 

You also need to define a function to load the file, since the assembly in response.download will not work:

 import contenttype as c def download(): if not request.args: raise HTTP(404) name = request.args[-1] field = db["files"]["file"] try: (filename, file) = field.retrieve(name) except IOError: raise HTTP(404) response.headers["Content-Type"] = c.contenttype(name) response.headers["Content-Disposition"] = "attachment; filename=%s" % name stream = response.stream(file, chunk_size=64*1024, request=request) raise HTTP(200, stream, **response.headers) 

To connect the dots, you need to create a form. In the example below, I use the new mesh mechanism, which is much better than the old school uniforms (but not yet documented in the book).

 upload = lambda filename: URL("download", args=[filename]) def index(): grid = SQLFORM.grid(db.files, onvalidation=validate, upload=upload) return {"grid":grid} 

If you don’t need any attachment to the grid, the equivalent controller code is:

 def index(): if len(request.args): form=SQLFORM(db.files, request.args[0], upload=URL("download")) else: form=SQLFORM(db.files, upload=URL("download")) if form.process(onvalidation=validate).accepted: response.flash = "files updated" return {"form":form} 
+6
source

so I did it :) here is my code

 import os upload_folder ='C:\\Python27\\web2py' sl = "\\" path = upload_folder + sl def display_form(): form = SQLFORM.factory( Field('file_name', requires=IS_NOT_EMPTY()), Field('file', 'upload',uploadfolder=upload_folder)) if form.accepts(request.vars, session): #.process().accepted: session.file_name= form.vars.file_name coded_name = form.vars.file orig_name = request.vars.file.filename os.rename(path + coded_name, path + orig_name) response.flash = u'datoteka naložena' elif form.errors: response.flash = 'form has errors' return dict(form=form) 

I know this is probably not the best solution, but since it works, I like it :)

thanks Anthony

+2
source

All Articles