20

現在、リクエスト中にいくつかの一時ファイルが操作されるサーバー側のjsonインターフェースを開発しています。

リクエストの最後にこれらのファイルをクリーンアップするための現在のソリューションは次のようになります。

@app.route("/method",methods=['POST'])
def api_entry():
    with ObjectThatCreatesTemporaryFiles() as object:
        object.createTemporaryFiles()
        return "blabalbal"

この場合、クリーンアップは object.__exit__() でレースを行います

ただし、いくつかのケースでは、一時ファイルをクライアントに返す必要があります。その場合、コードは次のようになります。

@app.route("/method",methods=['POST'])
def api_entry():
    with ObjectThatCreatesTemporaryFiles() as object:
        object.createTemporaryFiles()
        return send_file(object.somePath)

クリーンアップが行われると、フラスコはファイルを読み取ってクライアントに送信するプロセスにあるため、これは現在機能しません。¨ どうすればこれを解決できますか?

編集:ファイルが一時ディレクトリにあることを忘れていました。

4

5 に答える 5

14

私が使用した方法は、応答が完了したら弱参照を使用してファイルを削除することです。

import shutil
import tempfile
import weakref

class FileRemover(object):
    def __init__(self):
        self.weak_references = dict()  # weak_ref -> filepath to remove

    def cleanup_once_done(self, response, filepath):
        wr = weakref.ref(response, self._do_cleanup)
        self.weak_references[wr] = filepath

    def _do_cleanup(self, wr):
        filepath = self.weak_references[wr]
        print('Deleting %s' % filepath)
        shutil.rmtree(filepath, ignore_errors=True)

file_remover = FileRemover()

そしてフラスココールで私が持っていた:

@app.route('/method')
def get_some_data_as_a_file():
    tempdir = tempfile.mkdtemp()
    filepath = make_the_data(dir_to_put_file_in=tempdir)
    resp = send_file(filepath)
    file_remover.cleanup_once_done(resp, tempdir)
    return resp

これは非常に一般的であり、アプローチとして、私が使用した 3 つの異なる Python Web フレームワークで機能しました。

于 2015-08-21T03:44:18.297 に答える
11

Flask 0.9 以降を使用している場合は、after_this_requestデコレータを使用できます。

@app.route("/method",methods=['POST'])
def api_entry():
    tempcreator = ObjectThatCreatesTemporaryFiles():
    tempcreator.createTemporaryFiles()

    @after_this_request
    def cleanup(response):
        tempcreator.__exit__()
        return response

    return send_file(tempcreator.somePath)

編集

これではうまくいかないので、cStringIO代わりに以下を使用してみてください (これは、ファイルがメモリに収まるほど小さいことを前提としています)。

@app.route("/method", methods=["POST"])
def api_entry():
    file_data = dataObject.createFileData()
    # Simplest `createFileData` method:  
    # return cStringIO.StringIO("some\ndata")
    return send_file(file_data,
                        as_attachment=True,
                        mimetype="text/plain",
                        attachment_filename="somefile.txt")

または、現在行っているように一時ファイルを作成することもできますが、それらを削除するためにアプリケーションに依存する必要はありません。代わりに、約 1 時間ごとに実行される cron ジョブ (または Windows で実行している場合はスケジュールされたタスク) を設定し、30 分以上前に作成された一時ディレクトリ内のファイルを削除します。

于 2012-11-12T14:12:09.213 に答える
3

私には2つの解決策があります。


最初の解決策は、メソッド内のファイルを削除することですが、ファイルを__exit__閉じないでください。そうすれば、ファイル オブジェクトは引き続きアクセス可能であり、それを に渡すことができますsend_file

X-Sendfileこれは、ファイル名を使用するため、使用しない場合にのみ機能します。


2 番目の解決策は、ガベージ コレクターに依存することです。send_file削除時にファイルを消去するファイル オブジェクトに渡すことができます (__del__メソッド)。そうすれば、ファイルオブジェクトがpythonから削除されたときにのみファイルが削除されます。TemporaryFileまだ使用していない場合は、そのために使用できます。

于 2012-11-12T14:15:10.953 に答える
3

少し遅れていますが、これはmadjar の提案を使用して行ったことです (他の誰かがこれに出くわした場合に備えて)。これは、私が使用する小さなヘルパー関数です (パラメーターとして PyExcelerate Workbook オブジェクトを受け取ります)。これをケースに適応させることができます。tempfile.TemporaryFile を作成/構築する方法を変更するだけで準備完了です! Windows 8.1 および Ubuntu 12.04 でテスト済み。

def xlsx_to_response(wb, filename):
    f = tempfile.TemporaryFile()
    wb._save(f)
    f.seek(0)
    response = send_file(f, as_attachment=True, attachment_filename=filename,
                         add_etags=False)

    f.seek(0, os.SEEK_END)
    size = f.tell()
    f.seek(0)
    response.headers.extend({
        'Content-Length': size,
        'Cache-Control': 'no-cache'
    })
    return response
于 2014-04-23T04:07:30.870 に答える
0

Windows / Linux / Mac 対応ソリューション

私はweakrefs、フラスコ組み込みデコレータを試しましたが、何も機能しませんでした。

すべてのシステムで機能した唯一の考えは、io.BytesIOを使用してメモリ内に一時ファイルを作成することです

import os
import io
import tempfile
from multiprocessing import Process

import flask


def background_job(callback):
    task = Process(target=callback())
    task.start()

def send_temp_file(file_path: str, temp_dir: tempfile.TemporaryDirectory, remove_dir_after_send=True):
    with open(file_path, "rb") as f:
        content = io.BytesIO(f.read())
    response = flask.send_file(content,
                               as_attachment=True,
                               attachment_filename=os.path.split(file_path)[0])
    if remove_dir_after_send:
        background_job(temp_dir.cleanup)
    return response



app = flask.Flask(__name__)


@app.route("/serve_file/", methods=["GET"])
def serve_file():
    temp_dir = tempfile.TemporaryDirectory()
    file_path = os.path.join(temp_dir.name, "test.txt")
    with open(file_path, "w") as f:
        f.write("Hello World!")

    return send_temp_file(file_path, temp_dir)


if __name__ == "__main__":
    app.run(port=1337)
于 2021-07-01T09:18:36.787 に答える