2011-11-04 31 views
7

我要上傳與SQL.factory() 文件上傳我只是想保持原來的文件名 我的代碼目前是的web2py與原文件名

form = SQLFORM.factory(
    Field('file_name', requires=IS_NOT_EMPTY()), 
    Field('file', 'upload',uploadfolder=upload_folder)) 
if form.accepts(request.vars, session): #.process().accepted: 
    response.flash = u'File uploaded' 
    session.your_name = form.vars.file_name 
    session.filename = request.vars.file 
elif form.errors: 
    response.flash = 'form has errors' 
return dict(form=form) 

我猜session.filename = request.vars .file是你設置文件名的地方。爲什麼我得到自動生成的文件名no_data.smth.23u8o8274823zu4i2.smth

謝謝

回答

6

首先,request.vars.file是一個Python cgi.FieldStorage對象,所以session.filename = request.vars.file應導致錯誤。 request.vars.file.file是實際的文件對象,而request.vars.file.filename是上傳文件的原始名稱。

當您通過上傳字段上傳文件時,web2py會自動生成一個名爲「table_name.field_name.random_id.b16encoded_original_filename.extension」的新名稱。這是爲了防止目錄遍歷攻擊和啓用下載機制(需要知道表和字段名稱)。在SQLFORM.factory的情況下,沒有數據庫表名,所以它默認爲表名'no_table'。

你所顯示的代碼實際上不應該生成像'no_data.smth.23u8o8274823zu4i2.smth'這樣的文件名。該文件名意味着你明確告訴SQLFORM.factory使用表名'no_data'(通過其table_name參數)並且上傳字段名稱是'smth'。 (上面的代碼會產生以「no_table.file」文件名。)

筆記,web2py中自動獲取上傳的文件的原始名稱並將其編碼(使用b16encode)到新的文件名(它則原始解碼當使用內置下載機制時的文件名)。原始文件名也可以在form.vars.file.filename中找到。所以,你不一定需要用戶輸入文件名。但是,如果你想使用戶能夠輸入可能會與實際文件名不同,然後使用用戶輸入文件名的文件名,你可以創建表格之前添加以下內容:

if 'file' in request.vars and request.vars.file_name: 
    request.vars.file.filename = request.vars.file_name 

這將重新將上傳文件的文件名分配給用戶輸入的值,然後web2py會將用戶輸入的文件名編碼爲新文件名。但是,請注意,web2py依賴於文件擴展名在下載時適當地設置HTTP標頭,因此您可能需要添加一些邏輯來獲取原始文件擴展名,以防用戶無法輸入。

+0

與request.vars.name_of_file.filename我得到的原始文件名,但如何重命名上傳。我應該使用os.rename嗎? 我上傳不同的zip文件,所以他們需要name_of_file.zip謝謝 – Yebach

+0

您也可以跳過'form.accepts'並處理自己保存的文件。但是,不要使用用戶上傳的文件來完成此操作,因爲您將對目錄遍歷攻擊開放。 – Anthony

+0

如何設置web2py沒有文件編碼。事情是我想上傳的文件存儲在一個文件夾中的原始文件名,因爲我有另一個腳本然後處理它,文件名是重要的文件處理? – Yebach

2

,所以我做到了:) 這裏是我的代碼

import os 
upload_folder ='C:\\Python27\\web2py' 
sl = "\\" 
path = upload_folder + sl 

def display_form(): 

    form = SQLFORM.factory(
     Field('file_name', requires=IS_NOT_EMPTY()), 
     Field('file', 'upload',uploadfolder=upload_folder)) 


    if form.accepts(request.vars, session): #.process().accepted: 
     session.file_name= form.vars.file_name 
     coded_name = form.vars.file 
     orig_name = request.vars.file.filename 
     os.rename(path + coded_name, path + orig_name) 
     response.flash = u'datoteka naložena' 

    elif form.errors: 
     response.flash = 'form has errors' 
    return dict(form=form) 

我知道這可能不是最好的解決方案,但因爲它的作品,我喜歡:)

謝謝安東尼

6

如果您只是重命名文件,這將打破下載機制。此外,有時您可能想用不同於原始文件的名稱保存文件。讓我們假設你有以下型號:

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} 
+0

嗨我實現了上面的代碼工作正常,但「autodelete = True」不起作用。儘管該行從數據庫中被刪除...物理文件仍然存在。 – May