2011-11-04 9 views
7

chcę przesłać plik z SQL.factory() Chciałbym tylko, aby zachować oryginalną nazwę pliku mój kod jest obecnieWeb2py przesłać z oryginalnego pliku

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) 

Chyba session.filename = request.vars .file to miejsce, w którym ustawiłeś nazwę pliku. Dlaczego uzyskać wygenerowany automatycznie nazwę pliku no_data.smth.23u8o8274823zu4i2.smth

Dziękuję

Odpowiedz

6

Najpierw request.vars.file jest Python cgi.FieldStorage przedmiot, więc session.filename = request.vars.file powinno skutkować błędem. request.vars.file.file to rzeczywisty obiekt pliku, a request.vars.file.filename to oryginalna nazwa przesłanego pliku.

Po przesłaniu pliku za pomocą pola przesyłania, web2py automatycznie generuje nową nazwę formularza "nazwa_tabeli.nazwa_pole.random_id.b16wyznana_nazwa_nazwy_właściwości.rozszerzenie". Robi się to, aby zapobiec atakom przemierzania katalogów i włączyć mechanizm pobierania (który musi znać nazwę tabeli i pola). W przypadku SQLFORM.factory, nie ma nazwy tabeli bazy danych, więc domyślnie jest to nazwa tabeli "no_table".

Kod, który pokazałeś, nie powinien generować nazwy pliku, np. "No_data.smth.23u8o8274823zu4i2.smth". Nazwa pliku oznacza, że ​​użytkownik jawnie powiedział SQLFORM.factory, aby użyć nazwy tabeli "no_data" (za pomocą jej argumentu table_name), a nazwa pola przesyłania jest "smth". (Powyższy kod wygenerowałby nazwę pliku zaczynającą się od "no_table.file".)

Uwaga, web2py automatycznie pobiera oryginalną nazwę przesłanego pliku i koduje go (używając b16 kodu) do nowej nazwy pliku (następnie dekoduje oryginał nazwa pliku, gdy używany jest wbudowany mechanizm pobierania). Oryginalna nazwa pliku jest również dostępna w pliku form.vars.file.filename. Tak więc użytkownik niekoniecznie musi w ogóle podać nazwę pliku. Jednakże, jeśli chcesz, aby umożliwić użytkownikowi wprowadzić nazwę, która może różnić się od rzeczywistej nazwy pliku, a następnie użyć pliku wprowadzonych przez użytkownika, można dodać następujące przed stworzeniem forma:

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

To będzie ponownie przypisz nazwę przesłanego pliku do wartości wprowadzonej przez użytkownika, a następnie web2py zakoduje nazwę pliku wprowadzoną przez użytkownika do nowej nazwy pliku. Zauważ jednak, że web2py polega na rozszerzeniu nazwy pliku, aby odpowiednio ustawić nagłówki HTTP po pobraniu, więc możesz chcieć dodać trochę logiki, aby uzyskać oryginalne rozszerzenie nazwy pliku, na wypadek gdyby użytkownik nie wprowadził go.

+0

z request.vars.name_of_file.filename ja dostać oryginalną nazwę pliku, ale jak zmień nazwę przesłanego. powinienem zrobić z os.rename? Przesyłam różne pliki zip, więc muszą to być name_of_file.zip DZIĘKUJĘ – Yebach

+0

Można również pominąć 'form.accepts' i obsługiwać zapisywanie plików. Nie rób tego z plikami przesłanymi przez użytkownika, ponieważ będziesz otwarty na ataki przemierzania katalogów. – Anthony

+0

jak ustawić kodowanie plików przez web2py. Chodzi o to, że przesłany plik powinien być przechowywany w jednym folderze z oryginalną nazwą pliku, czy mam inny skrypt, aby go przetworzyć, a nazwa pliku jest ważna dla przetwarzania plików? – Yebach

2

więc to zrobiłem :) tutaj jest mój kod

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) 

wiem, że prawdopodobnie nie jest to najlepsze rozwiązanie, ale ponieważ działa, lubię :)

dziękuję Anthony

6

Jeśli zmienisz nazwę pliku, spowoduje to przerwanie mechanizmu pobierania. Co więcej, czasami możesz chcieć zapisać plik pod inną nazwą niż oryginał.Załóżmy, że masz następujący model:

db.define_table("files", 
Field("name", unique=True), 
Field("file", "upload")) 

należy rozszerzyć pole wysyłania doda sklepu i odzyskać funkcje:

Field("file", "upload", custom_store=store_file, custom_retrieve=retrieve_file) 

Funkcje te są po prostu pisanie/czytanie pliku z ustalonym katalogu przesyłania :

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')) 

Teraz w kontrolerze należy zmodyfikować plik form.vars przed włożeniem/aktualizacją bazy danych i ustawić nazwę pliku. Jeśli chcesz zachować oryginalną nazwę przesłanego pliku, nie jest to konieczne.

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 

Należy również zdefiniować funkcję, aby pobrać plik kompilacji w response.download nie będzie działać:

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) 

Aby połączyć kropki, trzeba zbudować formę. W poniższym przykładzie używam nowego mechanizmu siatki, który jest lepszy niż formularze starej szkoły (ale jeszcze nie udokumentowane w książce).

upload = lambda filename: URL("download", args=[filename]) 

def index(): 
    grid = SQLFORM.grid(db.files, onvalidation=validate, upload=upload) 
    return {"grid":grid} 

Jeśli nie chcesz cały fanciness siatki, odpowiednik kod kontrolera to:

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

Witam Zaimplementowałem powyższy kod, który działa poprawnie, ale "autodelete = True" nie działa. Chociaż wiersz zostanie usunięty z bazy danych ... plik fizyczny pozostaje. – May