私はダウンロードを提供する方法を使用してきましたが、それが安全ではなかったので、それを変更することにしました。 (この方法はストレージ内の元のファイルへのリンクでしたが、リンクを知っている全員がファイルをダウンロードできるというリスクがありました!)そこで、ビューを介してファイルを提供します。これにより、権限を持つユーザーのみがファイルをダウンロードできます。しかし、ファイルへの同時ダウンロード要求が多い間は、サーバーの負荷が高いことに気づきます。これは、ユーザーのダウンロードを処理する私のコードの一部です(ファイルはイメージであると考えてください)
image = Image.open ("the path to file")
response = HttpResponse(mimetype = 'image/png' )
response['Content-Disposition'] = 'attachment: filename=%s.png' % filename
image.save(response , "png")
return response
セキュリティを維持しながらサーバー側の負荷を下げながらファイルを提供するためのより良い方法はありますか?前もって感謝します :)
画像を開くと、画像がメモリに読み込まれます。これにより、頻繁に使用すると、負荷が増加します。 Martinが投稿したように、実際の解決策はファイルを直接提供することです。
次に、ファイルをメモリにロードせずにチャンクでストリーミングする別の方法を示します。
import os
import mimetypes
from Django.http import StreamingHttpResponse
from Django.core.servers.basehttp import FileWrapper
def download_file(request):
the_file = '/some/file/name.png'
filename = os.path.basename(the_file)
chunk_size = 8192
response = StreamingHttpResponse(FileWrapper(open(the_file, 'rb'), chunk_size),
content_type=mimetypes.guess_type(the_file)[0])
response['Content-Length'] = os.path.getsize(the_file)
response['Content-Disposition'] = "attachment; filename=%s" % filename
return response
この answer で説明されているように、「sendfile」メソッドを使用できます。
実際には、これが必要です(c&p):
response = HttpResponse(mimetype='application/force-download')
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It's usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response
これには mod_xsendfile が必要です(これは nginx または lighty でもサポートされています)
GZipMiddlewareがインストールされている場合(Django 1.4以下)、FileWrapperは機能しません: https://code.djangoproject.com/ticket/6027
GZipMiddlewareを使用する場合、実用的な解決策は、次のようにFileWrapperのサブクラスを記述することです。
from wsgiref.util import FileWrapper
class FixedFileWrapper(FileWrapper):
def __iter__(self):
self.filelike.seek(0)
return self
import mimetypes, os
my_file = '/some/path/xy.ext'
response = HttpResponse(FixedFileWrapper(open(my_file, 'rb')), content_type=mimetypes.guess_type(my_file)[0])
response['Content-Length'] = os.path.getsize(my_file)
response['Content-Disposition'] = "attachment; filename=%s" % os.path.basename(my_file)
return response
Python 2.5以降、DjangoからFileWrapperをインポートする必要はありません。
FileResposeを使用することをお勧めします。これは、バイナリファイル用に最適化されたStreamingHttpResponseのサブクラスです。 wsgiサーバーから提供されている場合はwsgi.file_wrapperを使用します。それ以外の場合は、ファイルを小さなチャンクでストリーミングします。
import os
from Django.http import FileResponse
from Django.core.servers.basehttp import FileWrapper
def download_file(request):
_file = '/folder/my_file.Zip'
filename = os.path.basename(_file)
response = FileResponse(FileWrapper(file(filename, 'rb')), content_type='application/x-Zip-compressed')
response['Content-Disposition'] = "attachment; filename=%s" % _file
return response
このようなリクエストを非常に少数しか提供しない場合を除き、Djangoを介してコンテンツを提供する必要があるソリューションはスケーラブルではありません。将来的に拡張するものについては、おそらくコンテンツストレージと検索配信を別のサーバーに移動したい場合、これは機能しません。
推奨される方法は、より軽量なサーバー(nginxなど)を通じて静的コンテンツを提供し続けることです。セキュリティを追加するには、Djangoからトークンを静的サーバーに渡します。これには、Cookieを設定するか、getパラメータを使用します。
トークンには、タイムスタンプ、ファイル名、ユーザーIDの値が必要です。 Django app。
次に、トークンをチェックし、ユーザーが実際にファイルにアクセスできることを確認する小さなnginxモジュールを記述します。また、タイムスタンプをチェックして、トークンが古くないこともチェックする必要があります。
ZipFly を使用できます
pip install zipfly
そう、
from Django.http import StreamingHttpResponse
import zipfly
# `filesystem` and `name` keys are required.
paths = [
{
'filesystem': 'file.mp4', # From your disk
'name': 'folder/file.mp4', # This is how it will appear in the Zip file
},
]
zfly = zipfly.ZipFly(paths=paths)
# new generator to streaming
z = zfly.generator()
response = StreamingHttpResponse(
z, content_type='application/octet-stream'
)
return response