Если вы просто переименуете файл, это нарушит механизм загрузки. Более того, иногда вы можете захотеть сохранить файл под другим именем, чем оригинал. Предположим, у вас есть следующая модель:
db.define_table("files",
Field("name", unique=True),
Field("file", "upload"))
Вам необходимо расширить поле загрузки с настраиваемыми функциями сохранения и получения:
Field("file", "upload", custom_store=store_file, custom_retrieve=retrieve_file)
Функции - это просто запись / чтение файла из фиксированной директории загрузки:
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'))
Теперь в контроллере необходимо изменить form.vars до того, как будет выполнена вставка / обновление базы данных, и задать имя файла. Если вы хотите сохранить исходное имя загруженного файла, в этом нет необходимости.
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
Вам также необходимо определить функцию для загрузки файла, поскольку сборка в response.download не будет работать:
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)
Чтобы соединить точки, вам нужно построить форму. В приведенном ниже примере я использую новый механизм сетки, который намного лучше, чем старые школьные формы (но еще не документирован в книге).
upload = lambda filename: URL("download", args=[filename])
def index():
grid = SQLFORM.grid(db.files, onvalidation=validate, upload=upload)
return {"grid":grid}
Если вы не хотите использовать всю сетку, код эквивалентного контроллера:
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}