web-dev-qa-db-ja.com

Flaskを使用したファイルのアップロードとダウンロード

PythonAnywhereとFlaskを使用して、ユーザーがテキストファイルをアップロードし、csvファイルを生成してから、csvファイルをダウンロードできるようにする、本当に単純なwebappを作成しようとしています。ドライブのtxtファイルからcsvを生成するためのプログラムをすでに作成しました。

今、私の関数はドライブ上のファイルを次のように開きます:

with open(INPUTFILE, "r") as fname:

そして、csvを次のように書き込みます。

with open(OUTPUTFILE, 'w') as fname:

iNPUTFILEおよびOUTPUTFILEはファイル名文字列です。

ファイルをオブジェクトとして扱い、フラスコ/ htmlによって返された方がいいのでしょうか?

これを行う方法がわかりません。このプログラムをどのように構成すればよいですか? HTMLテンプレートはいくつ必要ですか?ファイルをどこにでも保存せずに作業したいのですが、PythonAnywhereディレクトリに保存する必要がある場合はできます。どうやってやるの?

24
User1996

PythonAnywhere devはこちら。これはFlaskおよびWeb開発全般に関するものであり、システム固有のものではありません。そのため、私たち固有のものなしで一般的な回答をしようと思います:-)

あなたの質問に明確な答えを出すために知っておくべきことがいくつかあるので、私が作っている仮定をリストすることから始めます-それらのいずれかに間違っている場合はコメントを残し、答えを適切に更新します。

  • アップロードするファイルは巨大ではなく、妥当な量のメモリ(たとえば、メガバイト未満)に収まると想定しています。
  • テキストファイルからCSVを生成するために既に作成したプログラムはPythonにあり、以下を含む文字列を受け取る関数を持っている(または、おそらくもっと簡単に変更できる)と仮定していますテキストファイルのコンテンツ、およびCSVに書き込む必要があるコンテンツを返します。

これらの両方が当てはまる場合、Flaskアプリを構築する最良の方法は、Flask内のすべてを処理することです。コードサンプルは1000語に相当します。一緒に、ユーザーがテキストファイルをアップロードし、transform(変換プログラムからの関数が挿入される場所)と呼ばれる関数を介して実行し、ファイル全体で=,に置き換えます。ブラウザ PythonAnywhereにこのアプリのライブバージョンがあります

from flask import Flask, make_response, request

app = Flask(__name__)

def transform(text_file_contents):
    return text_file_contents.replace("=", ",")


@app.route('/')
def form():
    return """
        <html>
            <body>
                <h1>Transform a file demo</h1>

                <form action="/transform" method="post" enctype="multipart/form-data">
                    <input type="file" name="data_file" />
                    <input type="submit" />
                </form>
            </body>
        </html>
    """

@app.route('/transform', methods=["POST"])
def transform_view():
    request_file = request.files['data_file']
    if not request_file:
        return "No file"

    file_contents = request_file.stream.read().decode("utf-8")

    result = transform(file_contents)

    response = make_response(result)
    response.headers["Content-Disposition"] = "attachment; filename=result.csv"
    return response

他の質問について:

  • テンプレート:この例ではテンプレートを使用しませんでした。すべてのコードを1つのコードに収めたいからです。適切に実行していた場合は、formビューによって生成されたものをテンプレートに入れますが、それだけです。
  • ファイルに書き込むことでそれを行うことができます-はい、できます。アップロードされたファイルは、fileオブジェクトのsave(filename)メソッドを使用して保存できます。 streamプロパティを使用しています。ただし、ファイルが非常に小さい場合(上記の私の想定どおり)、おそらく上記のコードのようにメモリ内で処理する方が合理的です。

すべてお役に立てば幸いです。質問があればコメントを残してください。

31
Giles Thomas

追加する方が良い

response.headers["Cache-Control"] = "must-revalidate"
response.headers["Pragma"] = "must-revalidate"
response.headers["Content-type"] = "application/csv"

コンテンツタイプを追加しない場合、FF 48.0はそれをhtmlとして報告し、HTMLに対して1回、次にCSVに対して[保存]ダイアログを開きました。 Cache-Controlを追加しない場合、結果がキャッシュされる可能性があり、アクティブコンテンツを提供する場合、これは望みのものではありません。年齢なしでmust-revalidateを使用すると、実質的にno-cacheとして機能します-説明については here および here を参照してください。

7
teosbpl