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


245

サイトのユーザーが、直接ダウンロードできないようにパスが不明瞭なファイルをダウンロードできるようにしたい。

たとえば、URLは次のようにしたいとします。 http://example.com/download/?f=somefile.txt

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

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


2
なぜApacheを使ってこれを行わないのですか?Apacheは静的コンテンツをDjangoがこれまでよりも速く簡単に提供します。
S.Lott、2009

22
私はApacheを使用していません。なぜなら、Djangoに基づく許可なしにファイルにアクセスしたくないからです。
デーモン、2009

3
あなたはDjangoのビューを介してファイルを提供する必要があるアカウントのユーザー権限に撮りたい場合
ルカシュ

127
まさに、それが私がこの質問をしている理由です。
デーモン、2009

回答:


189

「両方の長所」については、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')  

編集:これをnginx確認する ために、X-Sendfileヘッダーの代わりに使用します。X-Accel-Redirectapache


6
ファイル名、またはpath_to_fileに「ä」や「ö」などの非ASCII文字が含まれている場合smart_str、ApacheモジュールX-Sendfileはsmart_strエンコードされた文字列をデコードできないため、意図したとおりに機能しません。したがって、たとえば「Örinää.mp3」ファイルは提供できません。また、smart_strを省略すると、すべてのヘッダーが送信前にASCII形式にエンコードされるため、Django自体がASCIIエンコードエラーをスローします。この問題を回避するために私が知っている唯一の方法は、X-sendfileのファイル名をASCIIのみで構成されるファイル名に減らすことです。
Ciantic、

3
より明確にするために、S.Lottには単純な例があり、djangoから直接ファイルを提供するだけで、他の設定は必要ありません。elo80kaには、Webサーバーが静的ファイルを処理し、djangoが処理する必要がない、より効率的な例があります。後者の方がパフォーマンスは優れていますが、さらに設定が必要になる場合があります。どちらにも場所があります。
rocketmonkeys

1
@Ciantic、エンコーディング問題の解決策のように見えるものについてはbtimbyの回答を参照してください。
mlissner 2012

このソリューションは、次のWebサーバー構成で機能しますか?バックエンド:相互に複製するように設定された2つ以上のApache + mod_wsgi個別(VPS)サーバー。フロントエンド:ラウンドロビンを行うアップストリームロードバランシングを使用する1つのnginxプロキシ(VPS)サーバー。
ダニエル

12
django 1.7のmimetypeはcontent_typeに置き換えられます
ismailsunni

88

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

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

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

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

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


49
これは、本質的に正しい(簡単な)答えは、しかし、1つの注意です-ユーザーが潜在的にダウンロードすることができ、パラメータの手段として、ファイル名を渡して任意の(。?つまり、あなたが「F = / etc / passwdファイルを」何を渡した場合)ファイルをたくさんありますこれを防ぐのに役立つもの(ユーザーのアクセス許可など)ですが、この明白だが一般的なセキュリティリスクに注意してください。これは基本的には検証入力のサブセットにすぎません。ビューにファイル名を渡す場合は、そのビューでファイル名を確認してください。
rocketmonkeys

9
非常にシンプルなこのセキュリティ問題の修正:filepath = filepath.replace('..', '').replace('/', '')
duality_

7
ダウンロードを許可するユーザーを含むファイル情報をテーブルに保存する場合、送信する必要があるのはファイル名ではなく主キーだけであり、アプリが実行する処理を決定します。
エドワードニューウェル

30

非常に単純ですが効率的またはスケーラブルなソリューションではない場合は、組み込みの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))

Windowsでのテストのフォールバックを提供するのにも非常に役立ちます。
Amir Ali Akbari 2014年

私は、デスクトップクライアントのように機能することを目的としたスタンドアロンのdjangoプロジェクトを実行しています。ありがとう!
daigorocub 2014年

1
なぜ効率的ではないのですか?
2014年

2
@zinkingファイルは通常、djangoプロセスではなく、Apacheのようなものを介して提供されるため
Cory

1
ここでは、どのようなパフォーマンス上の欠点について話しているのですか?ファイルがdjango経由で提供されている場合、ファイルはRAMに読み込まれますか?djangoがnginxと同じ効率で提供できないのはなぜですか?
Gershom 2015

27

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

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

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

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


4
そのスニペットは良くありません。django.core.servers.httpbaseこれは、最初に作成されて以来ファイル内に存在していたコード「DO N'T USE FOR PRODUCTION USE !!!」の上部に大きな警告サインがある、文書化されていないプライベートモジュールに依存しています。いずれにしても、このスニペットが依存する機能は、django 1.9で削除されました。FileWrapper
eykanal 2015

16

@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のおかげで、それらすべてを組み合わせた完璧なソリューションが得られました=)


1
ありがとう、これはまさに私が探していたものでした!
ihatecache

1
filename="%s"Content-Dispositionヘッダーのファイル名を二重引用符で囲み、ファイル名のスペースに関する問題を回避します。参考文献:スペースを含むファイル名は、ダウンロード時に切り捨てられHTTPのContent-dispositionヘッダのファイル名パラメータをエンコードするためにどのように?
クリスチャンロング

1
あなたの解決策は私のために働きます。しかし、ファイルに対して「無効な開始バイト...」エラーが発生しました。解決しましたFileWrapper(open(path.abspath(file_name), 'rb'))
Mark Mishyn

FileWrapperDjango 1.9から削除されました
freethebees 2017年

使用可能from wsgiref.util import FileWrapper
クリス

15

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

編集: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'),
...

1
どうもありがとうございました!バイナリファイルをダウンロードするための最も簡単なソリューションであり、機能します。
Julia Zhao

13

前述のように、mod_xsendfileメソッドでは、ファイル名に非ASCII文字を使用できません。

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

X-SendFile-Encoding: url

同様に送信されます。

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


パッチがコアライブラリに組み込まれました。
mlissner 2012

7

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

「Djangoがパーミッションなどをチェックしたら、Webサーバー(例:Apacheとmod_xsendfile)へのファイルアップロードをオフロードするための抽象化」


2
当時(1年前)、私の個人用フォークには、Apache以外のファイルを提供するフォールバックがあり、元のリポジトリにはまだ含まれていませんでした。
Roberto Rosario

リンクを削除したのはなぜですか?
kiok46 2015年

@ kiok46 Githubポリシーとの競合。正規アドレスを指すように編集されました。
Roberto Rosario

6

あなたのような人気のサーバによって与えられたのsendfileのAPIを使用する必要がありapachenginx 生産のを。長年、私はファイルを保護するためにこれらのサーバーのsendfile APIを使用していました。次に、開発と本番の両方の目的に適した、この目的のためのシンプルなミドルウェアベースのdjangoアプリを作成しました。ここからソースコードにアクセスできます。
更新:利用可能な場合、新しいバージョンのpythonプロバイダーはdjangoをFileResponse使用し、lighthttp、caddyからhiawathaまでの多くのサーバー実装のサポートも追加します

使用法

pip install django-fileprovider
  • fileproviderアプリを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/内部的にのみアクセスする場所のURL を設定します。上記の構成を使用している場合は、Xファイルを次のように設定できます。

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

これをnginx構成で行うことにより、ファイルが保護され、djangoからファイルを制御することもできます views


2

Djangoは、静的メディアを提供するために別のサーバーを使用することを推奨しています(同じマシンで実行されている別のサーバーは問題ありません)。彼らはlighttpなどのサーバーの使用を推奨しています。

これは設定が非常に簡単です。しかしながら。'somefile.txt'が要求に応じて生成された場合(コンテンツは動的)、djangoがそれを提供するようにしたい場合があります。

Djangoドキュメント-静的ファイル


2
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 

0

もう1つのプロジェクト: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)

1
request.user.is_authenticatedはメソッドであり、属性ではありません。(request.user.is_anonymous()ではない)is_authenticatedはis_anonymousの逆なので、request.user.is_authenticated()とまったく同じです。
爆発

@explodes最悪の場合でも、そのコードはdjango-private-files... のドキュメントから正しい
アルマンドペレスマルケス



0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.