サイトのユーザーに、パスが隠されているファイルをダウンロードできるようにして、直接ダウンロードできないようにします。
たとえば、URLを「 http://example.com/download/?f=somefile.txt のようにしたい
そして、サーバー上で、すべてのダウンロード可能なファイルがフォルダー「/ home/user/files /」にあることを知っています。
URLとビューを見つけて表示するのではなく、Djangoがダウンロード用にそのファイルを提供するようにする方法はありますか?
「両方の長所」については、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
を使用します。
「ダウンロード」は、単に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行未満のコードである必要があります。
非常に単純なただし、効率的でもスケーラブルでもないソリューションの場合、組み込みの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))
S.Lottには「良い」/単純なソリューションがあり、elo80kaには「最良の」/効率的なソリューションがあります。これは「より良い」/中間ソリューションです。サーバーのセットアップはありませんが、大きなファイルの場合は単純な修正よりも効率的です。
http://djangosnippets.org/snippets/365/
基本的に、Djangoはファイルの提供を引き続き処理しますが、一度にすべてをメモリにロードしません。これにより、サーバーはメモリ使用量を増やすことなく、(ゆっくり)大きなファイルを提供できます。
繰り返しになりますが、S.LottのX-SendFileは、大きなファイルに対しては依然として優れています。しかし、それを気にすることができない、またはしたくない場合、この中間のソリューションは、手間をかけずに効率を向上させます。
@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の回答に感謝し、それらすべてを組み合わせた完璧なソリューションを得ました=)
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'),
...
Mod_xsendfileメソッドでは、ファイル名にASCII以外の文字を使用できないことが上記で説明されました。
このため、名前がURLエンコードされている限り、任意のファイルを送信できるようにするmod_xsendfile用のパッチと追加ヘッダーがあります。
X-SendFile-Encoding: url
同様に送信されます。
試してください: https://pypi.python.org/pypi/Django-sendfile/
「Djangoが許可などを確認した後、ファイルのアップロードをWebサーバー(たとえば、Apacheとmod_xsendfile)にオフロードする抽象化」
実稼働環境では、Apache
やnginx
などの一般的なサーバーから提供されたsendfile APIを使用する必要があります。長年、ファイルを保護するためにこれらのサーバーのsendfile APIを使用していました。次に、開発と本番の両方の目的に適したこの目的のために、シンプルなミドルウェアベースのDjangoアプリを作成しました。ソースコード here にアクセスできます。
UPDATE:新しいバージョンでは、python
プロバイダーはDjango FileResponse
を使用し、lighthttp、caddy、hiawathaから多くのサーバー実装のサポートも追加します。
使用法
pip install Django-fileprovider
fileprovider
appをINSTALLED_APPS
設定に追加し、fileprovider.middleware.FileProviderMiddleware
をMIDDLEWARE_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
からファイルを制御することもできます。
Djangoは、別のサーバーを使用して静的メディアを提供することをお勧めします(同じマシンで実行されている別のサーバーでも構いません)。 lighttp のようなサーバーの使用をお勧めします。
これは非常に簡単に設定できます。しかしながら。リクエストに応じて「somefile.txt」が生成された場合(コンテンツは動的)、Djangoで提供することができます。
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
見るべき別のプロジェクト: 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)
私はこれについてプロジェクトをしました。あなたは私のgithubリポジトリを見ることができます:
https://github.com/nishant-boro/Django-rest-framework-download-expert
このモジュールは、ApacheモジュールXsendfileを使用して、Django restフレームワークでダウンロードするファイルを提供する簡単な方法を提供します。また、特定のグループに属するユーザーにのみダウンロードを提供する追加機能もあります
私は同じ問題に何度も直面しているため、xsendfileモジュールとauthビューデコレータ Django-filelibrary を使用して実装しました。独自のソリューションのインスピレーションとして自由に使用してください。