如果您只是重命名文件,则会破坏下载机制。此外,有时您可能希望以与原始文件不同的名称保存文件。假设您有以下模型:
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}