サイトのユーザーが、直接ダウンロードできないようにパスが不明瞭なファイルをダウンロードできるようにしたい。
たとえば、URLは次のようにしたいとします。 http://example.com/download/?f=somefile.txt
サーバーでは、ダウンロード可能なすべてのファイルがフォルダーにあることを知っています/home/user/files/
。
URLを見つけてそれを表示するのではなく、Djangoにダウンロード用のファイルを提供させる方法はありますか?
サイトのユーザーが、直接ダウンロードできないようにパスが不明瞭なファイルをダウンロードできるようにしたい。
たとえば、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')
編集:これをnginx
確認する
ために、X-Sendfileヘッダーの代わりに使用します。X-Accel-Redirect
apache
smart_str
、ApacheモジュールX-Sendfileはsmart_strエンコードされた文字列をデコードできないため、意図したとおりに機能しません。したがって、たとえば「Örinää.mp3」ファイルは提供できません。また、smart_strを省略すると、すべてのヘッダーが送信前にASCII形式にエンコードされるため、Django自体がASCIIエンコードエラーをスローします。この問題を回避するために私が知っている唯一の方法は、X-sendfileのファイル名をASCIIのみで構成されるファイル名に減らすことです。
「ダウンロード」は単に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行未満のコードにする必要があります。
filepath = filepath.replace('..', '').replace('/', '')
非常に単純ですが効率的またはスケーラブルなソリューションではない場合は、組み込みの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は、より大きなファイルに適しています。しかし、それができない、または気になりたくない場合は、この中間のソリューションを使用すると、手間をかけずに効率が向上します。
django.core.servers.httpbase
これは、最初に作成されて以来ファイル内に存在していたコード「DO N'T USE FOR PRODUCTION USE !!!」の上部に大きな警告サインがある、文書化されていないプライベートモジュールに依存しています。いずれにしても、このスニペットが依存する機能は、django 1.9で削除されました。FileWrapper
@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のおかげで、それらすべてを組み合わせた完璧なソリューションが得られました=)
filename="%s"
Content-Dispositionヘッダーのファイル名を二重引用符で囲み、ファイル名のスペースに関する問題を回避します。参考文献:スペースを含むファイル名は、ダウンロード時に切り捨てられ、HTTPのContent-dispositionヘッダのファイル名パラメータをエンコードするためにどのように?
FileWrapper(open(path.abspath(file_name), 'rb'))
FileWrapper
Django 1.9から削除されました
from wsgiref.util import FileWrapper
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'),
...
前述のように、mod_xsendfileメソッドでは、ファイル名に非ASCII文字を使用できません。
このため、名前がURLエンコードされていれば、どのファイルでも送信できるようにするmod_xsendfile用のパッチと、追加のヘッダーがあります。
X-SendFile-Encoding: url
同様に送信されます。
試してください:https : //pypi.python.org/pypi/django-sendfile/
「Djangoがパーミッションなどをチェックしたら、Webサーバー(例:Apacheとmod_xsendfile)へのファイルアップロードをオフロードするための抽象化」
あなたのような人気のサーバによって与えられたのsendfileのAPIを使用する必要がありapache
やnginx
生産のを。長年、私はファイルを保護するためにこれらのサーバーのsendfile APIを使用していました。次に、開発と本番の両方の目的に適した、この目的のためのシンプルなミドルウェアベースのdjangoアプリを作成しました。ここからソースコードにアクセスできます。
更新:利用可能な場合、新しいバージョンのpython
プロバイダーはdjangoをFileResponse
使用し、lighthttp、caddyからhiawathaまでの多くのサーバー実装のサポートも追加します
使用法
pip install django-fileprovider
fileprovider
アプリを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/
内部的にのみアクセスする場所のURL を設定します。上記の構成を使用している場合は、Xファイルを次のように設定できます。
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
もう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)
django-private-files
... のドキュメントから正しい
私は同じ問題に何度も直面したため、xsendfileモジュールとauthビューデコレータdjango-filelibraryを使用して実装しました。独自のソリューションのインスピレーションとして自由に使用してください。
https://github.com/johnsensible/django-sendfileを使用して静的htmlフォルダーへの保護されたアクセスを提供する:https : //gist.github.com/iutinvg/9907731
私はこれについてプロジェクトをしました。あなたは私のgithubリポジトリを見ることができます:
https://github.com/nishant-boro/django-rest-framework-download-expert
このモジュールは、ApacheモジュールXsendfileを使用して、django RESTフレームワークでダウンロードするファイルを提供する簡単な方法を提供します。また、特定のグループに属するユーザーにのみダウンロードを提供する追加機能もあります。