web-dev-qa-db-ja.com

Django)で大きなファイルを(高負荷で)提供する

私はダウンロードを提供する方法を使用してきましたが、それが安全ではなかったので、それを変更することにしました。 (この方法はストレージ内の元のファイルへのリンクでしたが、リンクを知っている全員がファイルをダウンロードできるというリスクがありました!)そこで、ビューを介してファイルを提供します。これにより、権限を持つユーザーのみがファイルをダウンロードできます。しかし、ファイルへの同時ダウンロード要求が多い間は、サーバーの負荷が高いことに気づきます。これは、ユーザーのダウンロードを処理する私のコードの一部です(ファイルはイメージであると考えてください)

    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  

セキュリティを維持しながらサーバー側の負荷を下げながらファイルを提供するためのより良い方法はありますか?前もって感謝します :)

34
user633784

画像を開くと、画像がメモリに読み込まれます。これにより、頻繁に使用すると、負荷が増加します。 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
56
Burhan Khalid

この 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 でもサポートされています)

14
Martin Thurau

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をインポートする必要はありません。

3

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
3
Joanna

このようなリクエストを非常に少数しか提供しない場合を除き、Djangoを介してコンテンツを提供する必要があるソリューションはスケーラブルではありません。将来的に拡張するものについては、おそらくコンテンツストレージと検索配信を別のサーバーに移動したい場合、これは機能しません。

推奨される方法は、より軽量なサーバー(nginxなど)を通じて静的コンテンツを提供し続けることです。セキュリティを追加するには、Djangoからトークンを静的サーバーに渡します。これには、Cookieを設定するか、getパラメータを使用します。

トークンには、タイムスタンプ、ファイル名、ユーザーIDの値が必要です。 Django app。

次に、トークンをチェックし、ユーザーが実際にファイルにアクセスできることを確認する小さなnginxモジュールを記述します。また、タイムスタンプをチェックして、トークンが古くないこともチェックする必要があります。

2
sharjeel

2020


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 
0
sandes