2011-11-04 7 views
7

元のファイル名でアップロードweb2pyの 私のコードは、現在は、私は元のファイル名を維持したいと思い、私は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自動生成されたファイル名を取得することはないまず、request.vars.fileは、Python cgi.FieldStorageオブジェクトなので、session.filename = request.vars.fileがエラーになるはずです

答えて

6

ありがとうございます。 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でファイルエンコーディングを設定しない方法。問題は、アップロードしたファイルを元のファイル名で1つのフォルダに保存したいからです。ファイルを処理するために別のスクリプトがあり、ファイル名がファイル処理にとって重要ですか? – 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を変更し、ファイル名を設定する必要があります。アップロードしたファイルの元の名前を保持したい場合は、これは必要ありません。あなたはまた、response.downloadでビルドが動作しませんよう、ファイルをダウンロードする機能を定義する必要が

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 

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

関連する問題