現在、さまざまなユーザーがファイルをアップロードし、アップロードしたファイルを変換し、変換したファイルをダウンロードできる小さな Web インターフェイスに取り組んでいます。私の質問では、変換の詳細は重要ではありません。
現在、アップロードされたファイルを管理するために flask-uploads を使用しており、それらをファイル システムに保存しています。ユーザーがファイルをアップロードして変換すると、アップロード フォルダーがいっぱいにならないように、ファイルを削除するためのさまざまなボタンが表示されます。
これは理想的ではないと思います。本当に欲しいのは、ファイルがダウンロードされた直後に削除されることです。セッションが終了したらファイルが削除されればそれでいいと思います。
この問題の解決方法を見つけるのに時間を費やしましたが、まだ成功していません。これは珍しい問題ではないようですので、私が見逃している解決策がどこかにあるに違いないと思います。誰か解決策を知っていますか?
ベストアンサー1
これを行うにはいくつかの方法があります。
send_file
その後すぐに削除する(Linuxのみ)
フラスコにはafter_this_request
このユースケースで機能するデコレータ:
@app.route('/files/<filename>/download')
def download_file(filename):
file_path = derive_filepath_from_filename(filename)
file_handle = open(file_path, 'r')
@after_this_request
def remove_file(response):
try:
os.remove(file_path)
file_handle.close()
except Exception as error:
app.logger.error("Error removing or closing downloaded file handle", error)
return response
return send_file(file_handle)
問題は、これがLinuxでのみ動作します(これにより、削除後でもファイルポインタが開いている場合はファイルを読み取ることができます)。また、いつも動作します (Flask によってファイルがリンク解除される前にカーネル呼び出しが行われないことがあるという報告を聞いたことがありますsend_file
)。ただし、ファイルを送信するために Python プロセスが拘束されることはありません。
ファイルをストリームしてから削除
理想的には、ファイルをクリーンアップしておく必要があります知るOSはそれをクライアントにストリーミングしています。これをするには、ファイルをストリーミングして閉じるジェネレータを作成し、Pythonを介してファイルをストリーミングします。この回答では:
def download_file(filename):
file_path = derive_filepath_from_filename(filename)
file_handle = open(file_path, 'r')
# This *replaces* the `remove_file` + @after_this_request code above
def stream_and_remove_file():
yield from file_handle
file_handle.close()
os.remove(file_path)
return current_app.response_class(
stream_and_remove_file(),
headers={'Content-Disposition': 'attachment', 'filename': filename}
)
このアプローチはクロスプラットフォームなので便利です。ただし、ファイル全体がクライアントにストリーミングされるまで Python Web プロセスが拘束されるため、万能薬ではありません。
タイマーで掃除する
タイマーで別のプロセスを実行する(cron
おそらく を使用)か、次のようなインプロセススケジューラを使用する。APSchedulerタイムアウト(例:30分、1週間、30日、RDMBSで「ダウンロード済み」とマークされてから)を超えてディスク上の一時場所に残っているファイルをクリーンアップします。
これは最も堅牢な方法ですが、追加の複雑さ(cron、インプロセス スケジューラ、作業キューなど)が必要になります。