[英]Having Django serve downloadable files
我希望網站上的用戶能夠下載路徑被隱藏的文件,因此無法直接下載。
例如,我希望 URL 是這樣的: http://example.com/download/?f=somefile.txt
://example.com/download/?f=somefile.txt
在服務器上,我知道所有可下載的文件都位於文件夾/home/user/files/
中。
有沒有辦法讓 Django 提供該文件以供下載,而不是試圖找到一個 URL 並查看來顯示它?
為了“兩全其美”,您可以將 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-Accel-Redirect
而不是apache
X-Sendfile 標頭。
“下載”只是 HTTP 標頭更改。
有關如何響應下載,請參閱http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment .
您只需要為"/download"
定義一個 URL。
請求的GET
或POST
字典將包含"f=somefile.txt"
信息。
您的視圖函數將簡單地將基本路徑與“ f
”值合並,打開文件,創建並返回一個響應對象。 它應該少於 12 行代碼。
對於一個非常簡單但不高效或可擴展的解決方案,您可以使用內置的 django serve
視圖。 這對於快速原型或一次性工作非常有用,但正如在整個問題中提到的那樣,您應該在生產中使用 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 的回答,得到了結合所有這些的完美解決方案 =)
只是提到了 Django 1.10 中可用的FileResponse對象
編輯:在尋找一種通過 Django 流式傳輸文件的簡單方法時遇到了我自己的答案,所以這里有一個更完整的例子(給未來的我)。 它假定 FileField 名稱是imported_file
視圖.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
網址.py
...
url(r'^somefile/(?P<filename>[-\w_\\-\\.]+)$', views.SomeFileDownloadView.as_view(), name='somefile-download'),
...
上面提到了 mod_xsendfile 方法不允許文件名中包含非 ASCII 字符。
出於這個原因,我有一個可用於 mod_xsendfile 的補丁,它允許發送任何文件,只要名稱是 url 編碼的,並且附加標頭:
X-SendFile-Encoding: url
也送了。
試試: https ://pypi.python.org/pypi/django-sendfile/
“一旦 Django 檢查了權限等,將文件上傳卸載到 Web 服務器(例如帶有 mod_xsendfile 的 Apache)的抽象。”
您應該在生產中使用流行的服務器(如apache
或nginx
)提供的 sendfile api。 多年來,我一直在使用這些服務器的 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
設置一個位置 url /files/
只能訪問內部,如果你使用上面的配置你可以設置X-File
為:
response['X-File'] = '/files/filename.extension'
通過使用 nginx 配置執行此操作,文件將受到保護,您還可以從 django views
中控制文件
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
Django 建議您使用另一台服務器來提供靜態媒體(在同一台機器上運行的另一台服務器很好。)他們建議使用諸如lighttp之類的服務器。
這很容易設置。 然而。 如果“somefile.txt”是根據請求生成的(內容是動態的),那么您可能希望 django 提供它。
我不止一次遇到同樣的問題,所以使用 xsendfile 模塊和 auth view decorators 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 框架中提供文件以供下載的簡單方法。 它還具有僅向屬於特定組的用戶提供下載的附加功能
另一個要看的項目: 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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.