web2py загрузить с оригинальным именем файла - PullRequest
7 голосов
/ 04 ноября 2011

Я хочу загрузить файл с помощью SQL.factory (). Я просто хотел бы сохранить исходное имя файла, мой код в настоящее время

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

Спасибо

Ответы [ 3 ]

6 голосов
/ 18 ноября 2011

Если вы просто переименуете файл, это нарушит механизм загрузки. Более того, иногда вы можете захотеть сохранить файл под другим именем, чем оригинал. Предположим, у вас есть следующая модель:

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}
6 голосов
/ 04 ноября 2011

Во-первых, 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 при загрузке, поэтому вы можете захотеть добавить некоторую логику для получения исходного расширения имени файла в случае, если пользователь не сможет его ввести.

2 голосов
/ 07 ноября 2011

так я и сделал :) вот мой код

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)

Я знаю, что это, вероятно, не лучшее решение, но так как оно работает, мне нравится :)1006 *

...