なぜかDjangoでファイルダウンロードする方法についてサイトが少なく、あったとしてもHttpResponseやStreamingHttpResponseで自前でやっちゃおうとしてるのが多い。
なのでFileResponseを使って楽にファイルダウンロード実現する方法まとめました。
目次
実装・サンプルコード
DjangoにはStreamingHttpResponseを継承するファイル用のFileResponseがあります。
これにファイルオブジェクト、ファイル名を渡してresponseとして返してあげるだけです。
ちなみにas_attachmentをFalseにすると、ブラウザ上に対象のファイルを表示するようになります。レスポンスヘッダのContent-Dispositionをinlineにするかattachmentにするかというだけですね。
from django.http import FileResponse
def file_download_view(request):
filename, filepath = <ファイル名、パスを取得>
return FileResponse(open(file_path, "rb"), as_attachment=True, filename=filename)
でurls.pyに以下のように記載すれば、<IP address:port>/files/downloadにアクセスすればファイルダウンロードできます。
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import path
from django.conf.urls.static import static
from django.conf import settings
from .views import file_download_view
urlpatterns = [
...,
path("files/download", file_download_view),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += staticfiles_urlpatterns()
パフォーマンスとかどうなの?
単純にファイルを読み込んでそれを返すとなるとメモリ消費が大きくなります。普通にHttpResponseとか使った実装方法だとそうなるかもしれません。
ただ、FileResponseはStreamingHttpResponse継承で、公式にもFileWrapper使っているのでチャンクごとにストリーミングします的記述があります。
https://docs.djangoproject.com/en/3.1/ref/request-response/#fileresponse-objects
なのでメモリ消費が大きくなるなどということはなさそうです。
実際に中身見た感じもiteratorなりGeneratorで遅延評価的な動きしてるっぽいです。
ファイル周り関連で、ImageField/FileField使うとき古いファイルが自動削除されない問題の解決策についてまとめているので、そちらも良ければ見てみてください!