如果你只是重命名文件,这样会破坏下载机制。此外,有时候你可能想将文件保存为不同于原始文件的名称。我们假设你有以下模型:
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):
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}