2011-12-22 22 views
25

私はダウンロード配信の方法を使用していましたが、安全ではなかったので変更することにしました。 (この方法はストレージ内の元のファイルへのリンクでしたが、そのリンクを持つ全員がファイルをダウンロードできたという危険性がありました!)ので、私はビューを介してファイルを提供します。しかし、ファイルの同時ダウンロード要求が多数あるうちに、サーバーの負荷が高いことに気付いています。ここではユーザーのためにダウンロードを扱う私のコードの一部は、 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 

は、セキュリティを維持しながらファイルを提供し、サーバ側の負荷を低減するための任意のより良い方法がある(ファイルが画像で考えてみましょう)ですか? ありがとうございます。

+0

画像を開いているのはなぜですか?もう一度保存するだけですか? –

+0

@burhanファイルにアクセスしてpng画像ファイルとして提供できるようにファイルを開いたので、画像を開かずにファイルを開くことはできますか? –

+1

すべてのクールな子供たちがmod_pythonを使用しなくなるまで、あなたはDjangoの認証システムから認証することができます:https://docs.djangoproject.com/en/dev/howto/apache-auth/しかし、今ではすべてのクールな子供たちがWSGIを使用していますとnginx)。それをベースにした解決策は、より広範なコミュニティに役立つでしょう – Spacedman

答えて

51

画像のあなたの開口部は、メモリにロードし、これは何が重い使用の下で負荷の増加を引き起こすか。実際のソリューションは、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 
+0

ありがとう、これは私が達成しようとしていたものです、私はApacheを使って直接提供することを知っていました。答え –

+0

ファイルが正常に生成されていますが、ファイル名の末尾にアンダースコアが追加されています.....例えばfile_name.txt_、name_view.pdf_などのように、ファイルの最後にこのアンダースコアを避ける方法名前? –

+1

この解決策は、FileWrapperがもう使用できないため、Django 1.9で動作します。輸入FileWrapper' wsgiref.utilのからDjangoの1.9 'で – azmeuk

13

この「answer」の説明に従って、「sendfile」メソッドを使用できます。

実際にあなたが(& P c)は、これを必要とする:

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 

これは(もnginxまたはlightyによってサポートされている)mod_xsendfileを必要

2

あなたがそのような要求の非常に非常に小さな数にサービスを提供しようとしている場合を除き、ジャンゴを経由してコンテンツを提供する必要が任意のソリューションは、スケーラブルではありません。将来的に規模を拡大するには、コンテンツストレージを移動して別のサーバーに配信する必要がありますが、これは機能しません。

静的コンテンツを軽量サーバー(nginxなど)に保存することをお勧めします。セキュリティを追加するには、クッキーを設定するか、getパラメータを使用して、静的サーバーにdjangoのトークンを渡します。

トークンは、timestamp、filename、useridの値を持つ必要があります。これは、djangoアプリケーションによっていくつかのキーを介して署名する必要があります。

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

3

のFileWrapper(以下ジャンゴ1.4と)GZipMiddlewareがインストールされているときに動作しません: 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をインポートする必要はありません。

2

FileResposeは、バイナリファイル用に最適化されたStreamingHttpResponseのサブクラスです。これはwsgi.file_wrapperをwsgiサーバーから提供されている場合は使用し、それ以外の場合は小さなチャンクでファイルをストリームします。

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 
+1

django 1.10用には、wsgiref.utilからインポートFileWrapper –

関連する問題