web-dev-qa-db-ja.com

Djangoにダウンロード可能なファイルを提供させる

サイトのユーザーに、パスが隠されているファイルをダウンロードできるようにして、直接ダウンロードできないようにします。

たとえば、URLを「 http://example.com/download/?f=somefile.txt のようにしたい

そして、サーバー上で、すべてのダウンロード可能なファイルがフォルダー「/ home/user/files /」にあることを知っています。

URLとビューを見つけて表示するのではなく、Djangoがダウンロード用にそのファイルを提供するようにする方法はありますか?

227
damon

「両方の長所」については、S.Lottのソリューションと xsendfileモジュール を組み合わせることができます。Djangoはファイル(またはファイル自体)へのパスを生成しますが、実際のファイルの提供はApache/Lighttpdによって処理されます。 mod_xsendfileを設定したら、ビューとの統合には数行のコードが必要です。

from Django.utils.encoding import smart_str

response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for Django 1.7
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が既に設定されている場合にのみ機能します。

編集:

mimetypeはDjango 1.7のcontent_typeに置き換えられます

response = HttpResponse(content_type='application/force-download'  

EDIT:nginx check this の場合、Apache X-Sendfileヘッダーの代わりに_X-Accel-Redirectを使用します。

184
elo80ka

「ダウンロード」は、単にHTTPヘッダーの変更です。

http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment を参照してくださいダウンロードで応答する方法について。

"/download"に必要なURL定義は1つだけです。

リクエストのGETまたはPOST辞書には"f=somefile.txt"情報が含まれます。

ビュー関数は、ベースパスと「f」値をマージし、ファイルを開いて、応答オブジェクトを作成して返します。 12行未満のコードである必要があります。

85
S.Lott

非常に単純なただし、効率的でもスケーラブルでもないソリューションの場合、組み込みのDjango serveビューを使用できます。これは、迅速なプロトタイプや1回限りの作業には優れていますが、この質問全体で述べたように、実稼働環境ではApacheやnginxなどを使用する必要があります。

from Django.views.static import serve
filepath = '/some/path/to/local/file.txt'
return serve(request, os.path.basename(filepath), os.path.dirname(filepath))
27
Cory

S.Lottには「良い」/単純なソリューションがあり、elo80kaには「最良の」/効率的なソリューションがあります。これは「より良い」/中間ソリューションです。サーバーのセットアップはありませんが、大きなファイルの場合は単純な修正よりも効率的です。

http://djangosnippets.org/snippets/365/

基本的に、Djangoはファイルの提供を引き続き処理しますが、一度にすべてをメモリにロードしません。これにより、サーバーはメモリ使用量を増やすことなく、(ゆっくり)大きなファイルを提供できます。

繰り返しになりますが、S.LottのX-SendFileは、大きなファイルに対しては依然として優れています。しかし、それを気にすることができない、またはしたくない場合、この中間のソリューションは、手間をかけずに効率を向上させます。

27
rocketmonkeys

@Rocketmonkeysソリューションを試しましたが、ダウンロードしたファイルは* .binとして保存され、ランダムな名前が付けられていました。もちろん、それは問題ありません。 @ elo80kaから別の行を追加すると、問題が解決しました。
現在使用しているコードは次のとおりです。

from wsgiref.util import FileWrapper
from Django.http import HttpResponse

filename = "/home/stackoverflow-addict/private-folder(not-porn)/image.jpg"
wrapper = FileWrapper(file(filename))
response = HttpResponse(wrapper, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename=%s' % os.path.basename(filename)
response['Content-Length'] = os.path.getsize(filename)
return response

これで、ファイルを(/ mediaまたは/ public_html内ではなく)プライベートディレクトリに保存し、Djangoを介して特定のユーザーまたは特定の状況で公開できます。
それが役に立てば幸い。

@ elo80ka、@ S.Lott、@ Rocketmonkeysの回答に感謝し、それらすべてを組み合わせた完璧なソリューションを得ました=)

15
Salvatorelab

FileResponse Django 1.10で利用可能なオブジェクトに言及するだけ

編集:Django経由でファイルをストリーミングする簡単な方法を探しているときに、私自身の答えに出くわしたので、ここに、より完全な例を示します(将来の私へ)。 FileField名はimported_fileであると想定しています

views.py

from Django.views.generic.detail import DetailView   
from Django.http import FileResponse
class BaseFileDownloadView(DetailView):
  def get(self, request, *args, **kwargs):
    filename=self.kwargs.get('filename', None)
    if filename is None:
      raise ValueError("Found empty filename")
    some_file = self.model.objects.get(imported_file=filename)
    response = FileResponse(some_file.imported_file, content_type="text/csv")
    # https://docs.djangoproject.com/en/1.11/howto/outputting-csv/#streaming-large-csv-files
    response['Content-Disposition'] = 'attachment; filename="%s"'%filename
    return response

class SomeFileDownloadView(BaseFileDownloadView):
    model = SomeModel

urls.py

...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
13
shadi

Mod_xsendfileメソッドでは、ファイル名にASCII以外の文字を使用できないことが上記で説明されました。

このため、名前がURLエンコードされている限り、任意のファイルを送信できるようにするmod_xsendfile用のパッチと追加ヘッダーがあります。

X-SendFile-Encoding: url

同様に送信されます。

http://ben.timby.com/?p=149

13
btimby

試してください: https://pypi.python.org/pypi/Django-sendfile/

「Djangoが許可などを確認した後、ファイルのアップロードをWebサーバー(たとえば、Apacheとmod_xsendfile)にオフロードする抽象化」

7
Roberto Rosario

実稼働環境では、Apachenginxなどの一般的なサーバーから提供されたsendfile APIを使用する必要があります。長年、ファイルを保護するためにこれらのサーバーのsendfile APIを使用していました。次に、開発と本番の両方の目的に適したこの目的のために、シンプルなミドルウェアベースのDjangoアプリを作成しました。ソースコード here にアクセスできます。
UPDATE:新しいバージョンでは、pythonプロバイダーはDjango FileResponseを使用し、lighthttp、caddy、hiawathaから多くのサーバー実装のサポートも追加します。

使用法

pip install Django-fileprovider
  • fileprovider appをINSTALLED_APPS設定に追加し、
  • fileprovider.middleware.FileProviderMiddlewareMIDDLEWARE_CLASSES設定に追加します
  • 本番環境でFILEPROVIDER_NAME設定をnginxまたはApacheに設定します。デフォルトでは、開発目的ではpythonです。

クラスベースビューまたは関数ビューで、応答ヘッダーX-File値をファイルへの絶対パスに設定します。例えば、

def hello(request):  
   // code to check or protect the file from unauthorized access
   response = HttpResponse()  
   response['X-File'] = '/absolute/path/to/file'  
   return response  

Django-fileproviderは、コードが最小限の変更のみを必要とするように改善されました。

Nginx設定

ファイルを直接アクセスから保護するために、構成を次のように設定できます。

 location /files/ {
  internal;
  root   /home/sideffect0/secret_files/;
 }

ここでnginxは場所を設定します/files/は内部的にのみアクセスします。上記の設定を使用している場合は、X-Fileを次のように設定できます。

response['X-File'] = '/files/filename.extension' 

Nginx設定でこれを行うことにより、ファイルは保護され、Django viewsからファイルを制御することもできます。

6
I Am Batman

Djangoは、別のサーバーを使用して静的メディアを提供することをお勧めします(同じマシンで実行されている別のサーバーでも構いません)。 lighttp のようなサーバーの使用をお勧めします。

これは非常に簡単に設定できます。しかしながら。リクエストに応じて「somefile.txt」が生成された場合(コンテンツは動的)、Djangoで提供することができます。

Django Docs-静的ファイル

2
kjfletch
def qrcodesave(request): 
    import urllib2;   
    url ="http://chart.apis.google.com/chart?cht=qr&chs=300x300&chl=s&chld=H|0"; 
    opener = urllib2.urlopen(url);  
    content_type = "application/octet-stream"
    response = HttpResponse(opener.read(), content_type=content_type)
    response["Content-Disposition"]= "attachment; filename=aktel.png"
    return response 

https://github.com/johnsensible/Django-sendfile を使用して静的htmlフォルダーへの保護されたアクセスを提供: https://Gist.github.com/iutinvg/9907731

0
iutinvg

見るべき別のプロジェクト: http://readthedocs.org/docs/Django-private-files/en/latest/usage.html ルックス約束して、まだ自分でテストしていない。

基本的に、プロジェクトはmod_xsendfile構成を抽象化し、次のようなことができるようにします。

from Django.db import models
from Django.contrib.auth.models import User
from private_files import PrivateFileField

def is_owner(request, instance):
    return (not request.user.is_anonymous()) and request.user.is_authenticated and
                   instance.owner.pk = request.user.pk

class FileSubmission(models.Model):
    description = models.CharField("description", max_length = 200)
        owner = models.ForeignKey(User)
    uploaded_file = PrivateFileField("file", upload_to = 'uploads', condition = is_owner)
0
thyagx

私はこれについてプロジェクトをしました。あなたは私のgithubリポジトリを見ることができます:

https://github.com/nishant-boro/Django-rest-framework-download-expert

このモジュールは、ApacheモジュールXsendfileを使用して、Django restフレームワークでダウンロードするファイルを提供する簡単な方法を提供します。また、特定のグループに属するユーザーにのみダウンロードを提供する追加機能もあります

0
nicks_4317

私は同じ問題に何度も直面しているため、xsendfileモジュールとauthビューデコレータ Django-filelibrary を使用して実装しました。独自のソリューションのインスピレーションとして自由に使用してください。

https://github.com/danielsokolowski/Django-filelibrary

0