簡體   English   中英

讓 Django 提供可下載文件

[英]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。

請求的GETPOST字典將包含"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

也送了。

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

試試: https ://pypi.python.org/pypi/django-sendfile/

“一旦 Django 檢查了權限等,將文件上傳卸載到 Web 服務器(例如帶有 mod_xsendfile 的 Apache)的抽象。”

您應該在生產中使用流行的服務器(如apachenginx )提供的 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設置為nginxapache ,默認情況下它是用於開發目的的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 提供它。

Django Docs - 靜態文件

我不止一次遇到同樣的問題,所以使用 xsendfile 模塊和 auth view decorators django-filelibrary 實現 隨意將其用作您自己解決方案的靈感。

https://github.com/danielsokolowski/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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM