2009-07-20 39 views
203

Tôi muốn người dùng trên trang web có thể tải xuống các tệp có đường dẫn bị che khuất để chúng không thể được tải xuống trực tiếp.Việc Django cung cấp các tệp có thể tải xuống

Ví dụ, tôi muốn URL đó thành một cái gì đó như thế này, "http://example.com/download/?f=somefile.txt

Và trên máy chủ, tôi biết rằng tất cả các file tải về cư trú trong một thư mục '/ home/user/files /'.

có cách nào để làm cho Django phục vụ tập tin đó để tải về như trái ngược với cố gắng tìm một URL và chế độ xem để hiển thị nó?

+2

Tại sao bạn không chỉ đơn giản là sử dụng Apache để làm điều này? Apache phục vụ nội dung tĩnh nhanh hơn và đơn giản hơn cả Django. –

+14

Tôi không sử dụng Apache vì tôi không muốn các tệp có thể truy cập mà không có quyền có trụ sở tại Django. – blackrobot

+3

Nếu bạn muốn đưa vào tài khoản người dùng quyền bạn phải phục vụ tập tin thông qua quan điểm của Django –

Trả lời

166

Đối với "tốt nhất của cả hai thế giới" bạn có thể kết hợp giải pháp S. Lott với các xsendfile module: django tạo ra đường dẫn đến tập tin (hoặc các tập tin riêng của mình), nhưng phục vụ tập tin thực tế được xử lý bởi Apache/Lighttpd. Một khi bạn đã thiết lập mod_xsendfile, tích hợp với quan điểm của bạn mất một vài dòng mã:

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 

Tất nhiên, điều này sẽ chỉ làm việc nếu bạn có thể kiểm soát máy chủ của bạn, hoặc công ty lưu trữ của bạn đã mod_xsendfile đã được thiết lập .

EDIT:

Mimetype được thay thế bằng content_type cho django 1,7

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

EDIT: Đối nginx séc this, nó sử dụng X-Accel-Redirect thay vì apache X-Sendfile tiêu đề.

+6

Nếu tên tệp của bạn hoặc path_to_file bao gồm các ký tự không phải ascii như "ä" hoặc "ö", 'smart_str' không hoạt động như dự định vì mô-đun apache X-Sendfile không thể giải mã chuỗi được mã hóa smart_str. Do đó, ví dụ: "Örinää.mp3" không thể phục vụ. Và nếu bỏ qua smart_str, chính Django sẽ ném lỗi mã hóa ascii vì tất cả * tiêu đề * được mã hóa thành định dạng ascii trước khi gửi. Cách duy nhất mà tôi biết để vượt qua vấn đề này là giảm các tên tập tin X-sendfile thành những tên chỉ bao gồm ascii. – Ciantic

+3

Để rõ ràng hơn: S.Lott có ví dụ đơn giản, chỉ phục vụ các tệp trực tiếp từ django, không cần thiết lập khác. elo80ka có ví dụ hiệu quả hơn, nơi máy chủ web xử lý các tệp tĩnh và django không phải. Sau này có hiệu suất tốt hơn, nhưng có thể yêu cầu thiết lập nhiều hơn. Cả hai đều có vị trí của họ. – rocketmonkeys

+1

@Ciantic, xem câu trả lời của btimby về những gì trông giống như một giải pháp cho vấn đề mã hóa. – mlissner

78

A "tải" chỉ đơn giản là một sự thay đổi tiêu đề HTTP.

Xem http://docs.djangoproject.com/en/dev/ref/request-response/#telling-the-browser-to-treat-the-response-as-a-file-attachment để biết cách respo với tải xuống.

Bạn chỉ cần một định nghĩa URL cho "/download".

Yêu cầu GET hoặc POST từ điển của bạn sẽ có thông tin "f=somefile.txt".

Chức năng chế độ xem của bạn sẽ đơn giản hợp nhất đường cơ sở với giá trị "f", mở tệp, tạo và trả về đối tượng phản hồi. Nó phải nhỏ hơn 12 dòng mã.

+40

Đây thực chất là câu trả lời đúng (đơn giản), nhưng một cách thận trọng - chuyển tên tệp thành tham số có nghĩa là người dùng có thể tải xuống bất kỳ tệp * nào (ví dụ: nếu bạn chuyển "f =/etc/passwd"?) Có rất nhiều thứ giúp ngăn chặn điều này (quyền của người dùng, vv), nhưng chỉ cần nhận thức được rủi ro bảo mật rõ ràng nhưng phổ biến này. Về cơ bản nó chỉ là một tập con của đầu vào xác nhận hợp lệ: Nếu bạn chuyển tên tệp vào một khung nhìn, hãy kiểm tra tên tệp trong chế độ xem đó! – rocketmonkeys

+6

Một sửa chữa rất đơn giản cho mối quan tâm bảo mật này: 'filepath = filepath.replace ('..', '') .replace ('/', '')' –

+4

Nếu bạn sử dụng một bảng để lưu trữ thông tin tệp, bao gồm người dùng có thể tải xuống, sau đó tất cả những gì bạn cần gửi là khóa chính, không phải tên tệp và ứng dụng quyết định việc cần làm. –

1

Django khuyên bạn nên sử dụng một máy chủ khác để phục vụ phương tiện tĩnh (một máy chủ khác chạy trên cùng một máy là tốt.) Họ khuyên bạn nên sử dụng các máy chủ như lighttp.

Điều này rất đơn giản để thiết lập. Tuy nhiên. nếu 'somefile.txt' được tạo theo yêu cầu (nội dung động) thì bạn có thể muốn django phục vụ nó.

Django Docs - Static Files

25

S.Lott có giải pháp "tốt"/đơn giản và elo80ka có giải pháp "tốt nhất"/hiệu quả.Đây là một "tốt hơn"/trung giải pháp - không có thiết lập máy chủ, nhưng hiệu quả hơn cho các tập tin lớn hơn so với sửa chữa ngây thơ:

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

Về cơ bản, Django vẫn xử lý phục vụ các tập tin nhưng không tải toàn bộ điều vào bộ nhớ cùng một lúc. Điều này cho phép máy chủ của bạn (từ từ) phục vụ một tệp lớn mà không làm tăng mức sử dụng bộ nhớ.

Một lần nữa, X-SendFile của S.Lott vẫn tốt hơn cho các tệp lớn hơn. Nhưng nếu bạn không thể hoặc không muốn bận tâm với điều đó, sau đó giải pháp trung gian này sẽ giúp bạn đạt được hiệu quả tốt hơn mà không gặp rắc rối.

+2

Đoạn mã đó không tốt. Điều đó dựa vào mô-đun riêng tư không có giấy tờ của 'django.core.servers.httpbase', có một dấu hiệu cảnh báo lớn ở đầu mã" [KHÔNG SỬ DỤNG CHO SẢN XUẤT SỬ DỤNG !!!] (https: // github. com/django/django/blob/master/django/core/máy chủ/basehttp.py) ", đã có trong tệp [vì nó được tạo lần đầu tiên] (https://github.com/django/django/blob/ b68c478aa5d890e76aae6e2f695220505618c8e0/django/core/servers/basehttp.py). Trong mọi trường hợp, chức năng 'FileWrapper' đoạn mã này dựa vào đã bị xóa trong django 1.9. – eykanal

12

Nó đã được đề cập ở trên rằng phương thức mod_xsendfile không cho phép ký tự không phải ASCII trong tên tệp.

Vì lý do này, tôi có một miếng vá có sẵn cho mod_xsendfile mà sẽ cho phép bất kỳ tập tin được gửi đi, chừng nào tên là url mã hóa, và tiêu đề bổ sung:

X-SendFile-Encoding: url 

Được gửi là tốt.

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

+0

Vá bây giờ được xếp vào thư viện corer. – mlissner

6

Hãy thử: https://pypi.python.org/pypi/django-sendfile/

"trừu tượng để giảm tải tập tin tải lên máy chủ web (ví dụ Apache với mod_xsendfile) một lần Django đã kiểm tra quyền, vv"

+2

Vào thời điểm đó (1 năm trước), ngã ba cá nhân của tôi có tệp không phải Apache phục vụ dự phòng, kho lưu trữ ban đầu chưa được bao gồm. –

+0

Tại sao bạn xóa liên kết? – kiok46

+0

@ kiok46 Xung đột với chính sách Github. Đã chỉnh sửa để trỏ đến địa chỉ chuẩn. –

0

Một dự án khác để xem tại: http://readthedocs.org/docs/django-private-files/en/latest/usage.html Có vẻ như đang promissing, chưa tự mình thử nghiệm.

Về cơ bản dự án tóm tắt cấu hình mod_xsendfile và cho phép bạn làm những việc như:

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) 
+1

request.user.is_authenticated là một phương thức, không phải là thuộc tính. (không request.user.is_anonymous()) là chính xác giống như request.user.is_authenticated() vì is_authenticated là nghịch đảo của is_anonymous. – explodes

+0

@explodes Thậm chí tệ nhất, mã đó là ngay từ các tài liệu của 'django-private-files' ... –

12

Cố gắng giải pháp @Rocketmonkeys nhưng tải về các tập tin đã được lưu trữ dưới dạng * .bin và được đặt tên ngẫu nhiên. Tất nhiên là không ổn. Thêm một dòng khác từ @ elo80ka đã giải quyết được sự cố.
Đây là mã Tôi đang sử dụng hiện nay:

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 

Bây giờ bạn có thể lưu trữ tập tin trong một thư mục riêng (không phải bên trong/media cũng không/public_html) và phơi bày chúng qua django cho người dùng nhất định hoặc trong những trường hợp nhất định.
Hy vọng điều đó sẽ hữu ích.

Nhờ @ elo80ka, @ S. Lott và @Rocketmonkeys cho câu trả lời, có giải pháp hoàn hảo kết hợp tất cả trong số họ =)

+1

Cảm ơn bạn, đây chính xác là những gì tôi đang tìm kiếm! – ihatecache

+1

Thêm dấu ngoặc kép xung quanh tên tệp 'filename ="% s "' trong tiêu đề Nội dung-Bố trí, để tránh các vấn đề về khoảng trống trong tên tệp. Tài liệu tham khảo: [Tên tệp có không gian bị cắt bớt khi tải xuống] (http://kb.mozillazine.org/Filenames_with_spaces_are_truncated_upon_download), [Làm cách nào để mã hóa tham số tên tệp của tiêu đề Nội dung-Xử lý trong HTTP?] (Http://stackoverflow.com/ question/93551/how-to-encode-the-filename-tham số-of-content-disposition-header-in-http) –

+1

Các giải pháp của bạn làm việc cho tôi. Nhưng tôi đã có lỗi "byte bắt đầu không hợp lệ ..." cho tệp của mình. Giải quyết nó bằng 'FileWrapper (mở (path.abspath (file_name), 'rb'))' –

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

Đối với một giải pháp rất đơn giản nhưng không hiệu quả hoặc khả năng mở rộng , bạn chỉ có thể sử dụng chế độ xem django serve được tích hợp sẵn. Điều này là tuyệt vời cho nguyên mẫu nhanh chóng hoặc một lần làm việc, nhưng như đã được đề cập trong suốt câu hỏi này, bạn nên sử dụng một cái gì đó như apache hoặc nginx trong sản xuất.

from django.views.static import serve 
filepath = '/some/path/to/local/file.txt' 
return serve(request, os.path.basename(filepath), os.path.dirname(filepath)) 
+0

Cũng rất hữu ích cho cung cấp dự phòng để thử nghiệm trên Windows. –

+0

Tôi đang làm một dự án django độc lập, có ý định làm việc giống như một ứng dụng khách trên máy tính để bàn và điều này hoạt động hoàn hảo. Cảm ơn! – daigorocub

+0

tại sao nó không hiệu quả? – zinking

4

Bạn nên sử dụng apis sendfile do máy chủ phổ biến như apache hoặc nginx trong sản xuất. Nhiều năm tôi đã sử dụng api sendfile của các máy chủ này để bảo vệ các tập tin. Sau đó, tạo một ứng dụng django dựa trên middleware đơn giản cho mục đích này phù hợp cho cả mục đích sản xuất &.You có thể truy cập mã nguồn here.
UPDATE: trong phiên bản mới python cung cấp sử dụng django FileResponse nếu có và cũng có thể thêm hỗ trợ cho việc triển khai nhiều máy chủ từ lighthttp, caddy để hiawatha

Cách sử dụng

pip install django-fileprovider 
  • thêm fileprovider ứng dụng INSTALLED_APPS thiết lập ,
  • thêm fileprovider.middleware.FileProviderMiddleware vào MIDDLEWARE_CLASSES cài đặt
  • đặt cài đặt FILEPROVIDER_NAME thành nginx hoặc apache trong sản xuất, theo mặc định là python cho mục đích phát triển.

trong chế độ xem lớp học hoặc chức năng của bạn đặt tiêu đề phản hồi X-File giá trị thành đường dẫn tuyệt đối cho tệp. Ví dụ:

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 impemented theo cách mã của bạn sẽ chỉ cần sửa đổi tối thiểu.

Nginx cấu hình

Để bảo vệ tập tin khỏi những truy cập trực tiếp bạn có thể thiết lập cấu hình như

location /files/ { 
    internal; 
    root /home/sideffect0/secret_files/; 
} 

đây nginx thiết lập một vị trí url /files/ chỉ truy cập internaly, nếu bạn đang sử dụng trên cấu hình bạn có thể đặt X-File là,

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

Làm theo ing này với cấu hình nginx, các tập tin sẽ được bảo vệ & bạn cũng có thể kiểm soát các tập tin từ django views

9

Chỉ cần nhắc đến đối tượng FileResponse sẵn trong Django 1.10

+1

Cảm ơn bạn rất nhiều! Đây là giải pháp đơn giản nhất để tải xuống các tệp nhị phân và nó hoạt động. –

Các vấn đề liên quan