# 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
#--- 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
)
)
@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!
# 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)
@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])
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