Hi!
Here is my solution for upload/download files with originals filename. I 
hope it help you.

1. Make  a *modUle* file (don't confuse with m*O*del ) with name  

*file_serv.py: *
# coding: utf8
#
# --------- file_serv.py -------

from gluon import *
import os
import shutil

# --------------------- custom store file with original file name 
---------------
# *BEWARE:* !!! this fun overwrites existing files without any warnings!!!  
# *BE CAREFUL*: !!! it not sanitize filename, but you MUST do it !!!
# it works properly with *English* filenames only (I couldn't to win utf-8 
win-style filenames with spaces and other nightmares - too lazy to do it yet
)

def store_file(file, filename=None, path=None):

    path=path or current._files_dir  # _files_dir - must be defined in any 
mOdel-file (in db.py for example). it's ABSOLUTE path
    # for relative path use *current.request.folder, it's == application 
folder*

    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 # filename is what will be store in db upload field (in 
fact it's just string-field) , so you can store file with name filename+... 
and return it instead
 

# --------------------- custom retrieve file with name=db.uploadfield 
---------------
# keep in mind that this fun is sharpened for call from ordinary web2py 
controller  - .../default/download 
# filename-arg (which is passed by .../default/download ) has a format: 
tablename.upfieldname.upfieldvalue
# upfieldvalue == filename was returned by function *store_file()* (see 
upper)

def retrieve_file( filename, path=None):

    path=path or current._files_dir # or something else 

    strip_filename=filename.split('.',2)[2] # fetch filename, i.e. remove 
tablename.upfieldname - prefix

    pathfilename=os.path.join(path,strip_filename)

    return (strip_filename, open(pathfilename,'rb') ) # strip_filename in 
returning - is just a suggestion, so it can be any what you like

2. Include in any model file at least:
#---  in any model file --------------------

import file_serv

_files_dir='Z:/any_folder/and_so_on/any_uploads_dir'
# or _files_dir=os.path.join(request.folder, 'any_relative_uploads_dir')

#represent fun returning filename compatible with ordinary default/download 
controller
def file_repr(v,r):
    href = URL('default','download', args=['tablename.upfieldname.%s'%v ])
    return  A(v, _href=href)

#the same but a little bit smarter: 
def file_img_repr(v,r):
    href=URL('default','download', args=['tablename.upfieldname.%s'%v ])
    if os.path.splitext(v)[-1].lower() in ('.tiff', '.tif', '.jpg', '.bmp', 
'.png', '.gif'): 
        return  DIV( 
                     DIV( A( v,_href=href)), # ref for download full image  
                     IMG(_src=href ) #  try add args:   
_style='width:200px' or _class='your_img_css_class' or  something else
                   )
    else:
        return  A(v, _href=href)


db.define_table( 'tablename',
                    Field('upfieldname', 'upload',  # 
db.tablename[id].upfieldname 
- return original filename  
                           uploadfolder = _files_dir,
                           custom_store = file_serv.store_file,
                           custom_retrieve = file_serv.retrieve_file,
                           represent = file_repr # or file_img_repr 
                          )
               )




3. That's all! but:

   -  This example will work only with DAL object with 'db'- name  (not 
   'my_db' or 'db_file_storage' or some one else ). If you want to solve this 
   problem you have a choice:
   - change default/download fun to:
      @cache.action()
      def download():
          """
          allows downloading of uploaded files
          http://..../[app]/default/download/[filename]
          """
          return response.download(request, *my_db_file_storage*) # 
      ATTENTION: all upload fields of other DAL objects (*including db*) *will 
      not work!*    
      
      - write your own default/download (just copy one upper and change it 
      to):
   

   - 
      # ATTENTION: in this case you must *explicitly* specify *URL* arg ( =
      URL('default','*my_download*') ) wherever it's used !
      # at least in the *file_repr()* definition: _href= URL('default','
      *my_download*', ...) and so on
      @cache.action()
      def *my_download():*
          return response.download(request, *my_db_file_storage*) 
      
          
      - or more web2pythonic:
      - 
      @cache.action()
      def download():
          """
          allows downloading of uploaded files
          http://..../[app]/default/download/[filename]
          """
          if not request.vars.db_file_storage: 
      return response.download(request, *db*) # download from *db* by 
      default
          else:
      # download from specify DAL obj which name is passed in 
      *db_file_storage* 
      # it's necessary *explicitly* specify *URL* due to pass DAL_obj_name 
      (if it's not 'db'):
      # = URL('default', 'download', vars=dict(
      *db_file_storage='any_DAL_obj_name'*))
      return response.download(request, *globals()[*
      *request.vars.db_file_storage]*) 
      
      

4. Some useful functions:

def upload_from_dir(path, fld_obj):
    """
    Copy files to fld_obj.uploadfolder and populate table with filenames 
from existing folder
    Args:
      - path - existing absolute path 
      - fld_obj - upload field-object, for example: db.table.my_files
    return filenames list 
    """

    from os import listdir
    from os.path import isfile, join
  
    all_files = [f for f in sorted(listdir(path)) if isfile(join(path,f))]
    for f_name in all_files:
        with open(join(path,f_name), 'rb') as stream: 
             fld_obj.table.insert( **{fld_obj.name:fld_obj.store_file(stream, 
f_name, fld_obj.uploadfolder)} )

    return all_files



def fake_upload_from_dir(path, fld_obj):
    """
    Populate table with filenames from existing folder
    Args:
      - path - existing absolute path 
      - fld_obj - upload field-object, for example: db.table.my_files
    return filenames list 
    """

    from os import listdir
    from os.path import isfile, join
    all_files = [f for f in sorted(listdir(path)) if isfile(join(path,f))]
    for f_name in all_files:
        #stream = open(join(path,f_name), 'rb')
        fld_obj.table.insert(**{fld_obj.name:f_name})
    return all_files





On Wednesday, August 12, 2015 at 3:00:27 PM UTC+3, Vladimiras Lomovas wrote:
>
>
> <https://lh3.googleusercontent.com/-Fw4lgUUChZE/Vcs1x2WWAzI/AAAAAAAAAC8/488bLLkzOdk/s1600/Selection_005.png>
>
>
> <https://lh3.googleusercontent.com/-5NBxevJLglM/Vcs2CygnxgI/AAAAAAAAADE/jRtUSIJ6Gd0/s1600/pic2.png>
> Hello Anthony,
> I'll try to do according to the book but unfortunately it won't work.
>
> import os
> user_name = request.vars['user_name']
> path_ = "/var/www-data/web2py/applications/app_name/uploads/%s" % user_name
> db.define_table('media',
>     Field('user_name', 'string', length=30, required=True),
>     Field('file_name', 'string', length=30, required =True),
>     Field('file_', uploadfolder=path)) 
> #uploadfolder=os.path.join(request.user_name, path))), 'upload')
>
> Upload function in the controller
>
> # Create folder(name it as username) upload file to that folder.
> def upload_File():
>     if request.vars['user_name'] is not None:
>         user_name = request.vars['user_name']
>         file_name = request.vars['file_name']
>         row = db(db.driver.user_name==user_name).select().first()
>         path = "/var/www-data/web2py/applications/app_name/uploads/%s" % 
> user_name
>         if row is not None:
>             if not os.path.exists(path):
>                 os.makedirs(path)
>             if request.vars['upload'] is not None:
>                 uploadedFile = request.vars['upload']
>                 inserted_id = 
> db.media.insert(user_name=user_name,file_name=file_name, file_=uploadedFile)
>                 return "Inserted file ID: %s, folder created %s" % 
> (inserted_id, user_name)
>     return 'Username "%s" does not exist or file wasn\'t chosen' % 
> user_name
>
> if i user 'upload' file uploaded in the uploads folder (as it should) and 
> I can download file by clicking "file" link in the "Database db select" as 
> shown in the pic 1
>
> https://lh3.googleusercontent.com/-Fw4lgUUChZE/Vcs1x2WWAzI/AAAAAAAAAC8/488bLLkzOdk/s1600/Selection_005.png
>
> But if I use:
>
> import os
> user_name = request.vars['user_name']
> path_ = "/var/www-data/web2py/applications/app_name/uploads/%s" % user_name
> db.define_table('media',
>     Field('user_name', 'string', length=30, required=True),
>     Field('file_name', 'string', length=30, required =True),
>     Field('file_', uploadfolder=path)) # or this -> 
> uploadfolder=os.path.join(request.user_name, path))),
>
> In "database db select" changes file downloading link "file" as shown in 
> pic2.:
>
> https://lh3.googleusercontent.com/-5NBxevJLglM/Vcs2CygnxgI/AAAAAAAAADE/jRtUSIJ6Gd0/s1600/pic2.png
>
>
> is this therefore download function? if it is how should I modify it or 
> create new one? if not please redirect to the solution.
> Thank you.
>
>
> On Monday, August 3, 2015 at 11:33:56 PM UTC+3, Anthony wrote:
>>
>> You could follow this method to store the original filenames: 
>> http://web2py.com/books/default/chapter/29/07/forms-and-validators#Storing-the-original-filename
>> .
>>
>> Anthony
>>
>> On Monday, August 3, 2015 at 3:52:19 PM UTC-4, Vladimiras Lomovas wrote:
>>>
>>> Thank you for such quick answer.
>>> Could you suggest solution(or where to look) on returning list of 
>>> uploaded files(without renaming them or otherwise) for the user to view or 
>>> download them?
>>>
>>>
>>> On Monday, August 3, 2015 at 3:55:12 AM UTC+3, Massimo Di Pierro wrote:
>>>>
>>>> web2py exposes only actions, not all functions. An action is a function 
>>>> in a controller that takes no arguments and does not start with two 
>>>> underscores.
>>>>
>>>> Be very careful with using the original filename as name of the file. 
>>>> There are many possible vulnerabilities associated with this behavior, 
>>>> including directory traversal. This is why most frameworks, including 
>>>> web2py, rename files on upload. Consider that the sender can inject any 
>>>> character it in filename and that character may have a special meaning on 
>>>> your filesystem (think of / \ : for example). Also consider a file with 
>>>> that name many already exist.
>>>>
>>>> Massimo
>>>>
>>>>
>>>> On Sunday, 2 August 2015 18:56:37 UTC-5, Vladimiras Lomovas wrote:
>>>>>
>>>>>
>>>>> <https://lh3.googleusercontent.com/-rj-e4oMWCIk/Vb5xxX8VZRI/AAAAAAAAACo/pnTUX_lqYA0/s1600/nolist.jpg>
>>>>>
>>>>> Hello I'm new to programming and web2py but here I'm doing my first 
>>>>> project.
>>>>> I'm using web2py as a back-and for android app to get and transfer 
>>>>> data.
>>>>> I need to return list of user uploaded files( in JSON ) but web2py 
>>>>> save files with unique name.
>>>>> I find most accurate solution to my problem: 
>>>>> http://stackoverflow.com/questions/8008213/web2py-upload-with-original-filename
>>>>>  
>>>>> but it's not working for some reason
>>>>> and some functions is not listed in "exposes:"(shwon in picture). why?
>>>>>
>>>>>

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to