2009-07-20 181 views
203

我希望網站上的用戶能夠下載路徑被遮蓋的文件,以便它們不能直接下載。讓Django提供可下載的文件

舉例來說,我想的網址是這樣的,「http://example.com/download/?f=somefile.txt

和服務器上,我知道,所有下載的文件駐留在一個文件夾‘/ home/user中/文件/’。

有沒有一種方法,使Django的服務該文件進行下載,而不是試圖找到一個URL和視圖來顯示它?

+2

爲什麼可用FileResponse對象的文件? Apache比Django能夠更快,更簡單地提供靜態內容。 – 2009-07-20 23:08:03

+14

我沒有使用Apache,因爲我不希望沒有基於Django的權限訪問這些文件。 – blackrobot 2009-07-21 13:47:50

+3

如果你想考慮用戶權限,你必須通過Django的視圖 – 2009-07-21 13:51:02

回答

166

對於「兩全其美」,你可以用xsendfile module結合美國洛特的解決方案:Django的生成路徑文件(或文件本身),但實際的文件服務被處理阿帕奇/ 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由CONTENT_TYPE替換爲django的1.7

response = HttpResponse(content_type='application/force-download' 

編輯: 對於nginx檢查this,它使用代替X-Accel-Redirectapache X-SENDFILE頭。

+6

如果您的文件名或path_to_file包含諸如「ä」或「ö」之類的非ascii字符,那麼由於apache模塊X-Sendfile無法解碼smart_str編碼的字符串,所以`smart_str`不能正常工作。因此,例如「Örinää.mp3」文件無法送達。如果你忽略了smart_str,Django本身會拋出ascii編碼錯誤,因爲在發送之前所有的* headers *都被編碼爲ascii格式。我只知道規避這個問題的方法是將X-sendfile文件名減少爲僅由ascii組成的文件名。 – Ciantic 2010-05-31 16:13:33

+3

更清楚的是:S.Lott有一個簡單的例子,就是直接從django提供文件,不需要其他設置。 elo80ka提供了一個更有效的例子,web服務器處理靜態文件,而django不需要。後者具有更好的性能,但可能需要更多的設置。兩者都有自己的位置。 – rocketmonkeys 2010-12-30 19:12:14

+1

@Ciantic,看看btimby的答案是什麼看起來像解決方案的編碼問題。 – mlissner 2012-02-14 07:36:56

78

「下載」是一個簡單的HTTP標頭的變化。

http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment如何respo nd下載。

您只需要一個URL定義"/download"

該請求的GETPOST字典將有"f=somefile.txt"信息。

您的視圖函數將簡單地將基本路徑與「f」值合併,打開文件,創建並返回響應對象。它應該少於12行代碼。

+40

這實際上是正確的(簡單的)答案,但是一個警告 - 將文件名作爲參數傳遞意味着用戶可以下載任何*文件(即,如果通過「f =/etc/passwd」會怎麼樣?)有很多事情可以幫助防止這種情況(用戶權限等),但只是意識到這種明顯但常見的安全風險。它基本上只是驗證輸入的一個子集:如果您將文件名傳遞給視圖,請在該視圖中檢查文件名! – rocketmonkeys 2010-12-30 19:10:40

+6

這個安全問題很簡單: `filepath = filepath.replace('..','').replace('/','')` – 2013-07-23 20:24:57

+4

如果您使用表來存儲文件信息,包括用戶應該能夠下載它,然後你需要發送的只是主鍵,而不是文件名,應用程序決定要做什麼。 – 2015-05-05 04:25:41

1

Django建議您使用另一臺服務器來提供靜態介質(在同一臺機器上運行的另一臺服務器很好。)他們推薦使用lighttp等服務器。

這是非常簡單的設置。然而。如果根據請求生成'somefile.txt'(內容是動態的),那麼您可能需要django來提供它。

Django Docs - Static Files

25

S.Lott擁有「優秀」/簡單的解決方案,而elo80ka擁有「最佳」/高效的解決方案。這裏是一個「更好」 /中間解決方案 - 沒有服務器的設置,但對於大文件比天真的修復更高效:

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

基本上,Django的仍然處理服務的文件,但整個事情不裝入記憶一次。這可以讓您的服務器(緩慢)提供大文件,而不會增加內存使用量。

同樣,對於較大的文件,S.Lott的X-SendFile仍然更好。但是,如果你不能或不想爲此煩惱,那麼這種中間解決方案將會在沒有麻煩的情況下爲你帶來更好的效率。

12

上面提到的是,mod_xsendfile方法不允許在文件名中使用非ASCII字符。

出於這個原因,我有可供mod_xsendfile一個修補程序,將允許發送的任何文件,只要名稱是URL編碼,並附加頭:

X-SendFile-Encoding: url 

發送爲好。

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

0

另一個項目來看看: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) 
12

嘗試@Rocketmonkeys解決方案,但下載的文件都被存儲爲* .bin文件並給予隨機的名字。這當然不好。從@ elo80ka添加另一行解決了這個問題。
這裏是我現在使用的代碼:

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 

現在,您可以將文件存儲在私有目錄(而不是內部/媒體也不/的public_html),並通過Django的將其暴露在一定的用戶或在某些情況下。
希望它有幫助。

感謝@ elo80ka,@美國洛特和@Rocketmonkeys的答案,得到了完美的解決方案結合個個=)

1
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 
23

對於一個非常簡單的,但效率不高或可擴展的解決方案 ,你可以使用內置的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)) 
4

您應該使用流行的服務器,比如在生產apachenginx 給出的sendfile的API。多年來,我使用這些服務器的sendfile api來保護文件。然後創建一個簡單的中間件基於django應用程序爲此目的適合發展&生產purpose.You可以訪問源代碼here
更新:在新版本python提供商使用的Django FileResponse如果有,也增加了對從lighthttp,球童多服務器部署到海華沙

使用

pip install django-fileprovider 
  • fileprovider應用程序添加到INSTALLED_APPS設置,
  • add fileprovider.middleware.FileProviderMiddlewareMIDDLEWARE_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/只能訪問internaly,如果你使用上面的配置,你可以設置爲X文件爲,

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

通過做nginx的配置荷蘭國際集團這一點,該文件將被保護&,你也可以不只是你使用Apache要做到這一點控制從Django的views

9

只是提在Django 1.10