2011-12-22 31 views
25

Tôi đã sử dụng một phương pháp để phân phát tải xuống nhưng vì nó không an toàn nên tôi đã quyết định thay đổi điều đó. (phương pháp này là liên kết đến tệp gốc trong bộ nhớ, nhưng rủi ro là mọi người có liên kết đều có thể tải xuống tệp!) vì vậy bây giờ tôi phân phát tệp qua các chế độ xem của tôi, theo cách đó, chỉ người dùng có quyền mới có thể tải tệp xuống, nhưng tôi nhận thấy một tải cao trên máy chủ trong khi có nhiều yêu cầu tải xuống đồng thời cho các tệp. đây là một phần của mã của tôi xử lý tải xuống cho người dùng (Xem xét tệp là hình ảnh)Cung cấp các tệp lớn (có tải cao) ở Django

image = Image.open ("the path to file") 
    response = HttpResponse(mimetype = 'image/png') 
    response['Content-Disposition'] = 'attachment: filename=%s.png' % filename 
    image.save(response , "png") 
    return response 

có cách nào tốt hơn để phân phối tệp trong khi vẫn giữ an toàn và hạ tải phía máy chủ không? cảm ơn trước :)

+0

Tại sao bạn mở hình ảnh, chỉ để lưu lại? –

+0

@burhan Tôi đã mở tệp để tôi có thể truy cập tệp và phân phát dưới dạng tệp hình ảnh png, có thể hoàn thành mà không cần mở hình ảnh không? –

+1

Cho đến khi tất cả các trẻ em mát mẻ ngừng sử dụng mod_python bạn có thể nhận được apache để xác thực từ hệ thống auth của Django: https://docs.djangoproject.com/en/dev/howto/apache-auth/ nhưng bây giờ tất cả các trẻ em mát mẻ sử dụng WSGI (và nginx). Các giải pháp dựa trên những thứ đó sẽ hữu ích cho cộng đồng rộng lớn hơn, – Spacedman

Trả lời

51

mở của bạn của tải hình ảnh nó trong bộ nhớ và đây là nguyên nhân làm tăng tải trọng khi sử dụng nhiều. Như được đăng bởi Martin giải pháp thực sự là để phục vụ các tập tin trực tiếp.

Dưới đây là một cách tiếp cận khác, điều này sẽ truyền tệp của bạn theo từng phần mà không tải nó vào bộ nhớ.

import os 
import mimetypes 
from django.http import StreamingHttpResponse 
from django.core.servers.basehttp import FileWrapper 


def download_file(request): 
    the_file = '/some/file/name.png' 
    filename = os.path.basename(the_file) 
    chunk_size = 8192 
    response = StreamingHttpResponse(FileWrapper(open(the_file, 'rb'), chunk_size), 
          content_type=mimetypes.guess_type(the_file)[0]) 
    response['Content-Length'] = os.path.getsize(the_file)  
    response['Content-Disposition'] = "attachment; filename=%s" % filename 
    return response 
+0

Cảm ơn bạn, điều này giống như những gì tôi đang cố gắng đạt được, tôi biết về việc phục vụ trực tiếp bằng Apache, muốn biết cách làm tốt hơn với chế độ xem django :) cảm ơn Câu trả lời –

+0

Ở đây tệp được tạo thành công nhưng với dấu gạch dưới được thêm vào cuối tên tệp ..... như file_name.txt_, name_view.pdf_ v.v., vậy làm thế nào để tránh dấu gạch dưới này ở cuối tệp Tên ? –

+1

Giải pháp này làm với Django 1.9 như FileWrapper không còn nữa. – azmeuk

13

Bạn có thể sử dụng phương thức 'sendfile' như được mô tả trong answer này.

Thực tế bạn cần điều này (c & p):

response = HttpResponse(mimetype='application/force-download') 
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 

Điều này đòi hỏi mod_xsendfile (mà cũng được hỗ trợ bởi nginx hoặc lighty)

2

Trừ khi bạn sắp phân phát rất ít yêu cầu như vậy, bất kỳ giải pháp nào yêu cầu phân phối nội dung của bạn qua django sẽ không thể mở rộng được. Đối với bất cứ điều gì để quy mô trong tương lai, có thể bạn sẽ muốn di chuyển lưu trữ nội dung của bạn và phục vụ đến một máy chủ riêng biệt và sau đó điều này sẽ không hoạt động.

Cách được khuyến nghị là giữ nội dung tĩnh được phân phối qua máy chủ nhẹ hơn (chẳng hạn như nginx). Để thêm bảo mật, hãy chuyển máy chủ tĩnh một mã thông báo từ django bằng cách đặt cookie hoặc thông qua các tham số get.

Mã thông báo phải có các giá trị sau: dấu thời gian, tên tệp, userid. Nó phải được ký thông qua một số phím bằng ứng dụng django.

Tiếp theo, viết mô-đun nginx nhỏ để kiểm tra mã thông báo và người dùng thực sự truy cập vào tệp. Bạn cũng nên kiểm tra mã thông báo đó không đủ tuổi bằng cách kiểm tra dấu thời gian.

3

FileWrapper sẽ không hoạt động khi GZipMiddleware được cài đặt (Django 1.4 và dưới đây): https://code.djangoproject.com/ticket/6027

Nếu sử dụng GZipMiddleware, một giải pháp thực tế là để viết một lớp con của FileWrapper như vậy:

from wsgiref.util import FileWrapper 
class FixedFileWrapper(FileWrapper): 
    def __iter__(self): 
     self.filelike.seek(0) 
     return self 

import mimetypes, os 
my_file = '/some/path/xy.ext' 
response = HttpResponse(FixedFileWrapper(open(my_file, 'rb')), content_type=mimetypes.guess_type(my_file)[0]) 
response['Content-Length'] = os.path.getsize(my_file) 
response['Content-Disposition'] = "attachment; filename=%s" % os.path.basename(my_file) 
return response 

Theo Python 2.5, không cần phải nhập FileWrapper từ Django.

2

Tốt hơn là sử dụng FileRespose, là một phân lớp của StreamingHttpResponse được tối ưu hóa cho các tệp nhị phân. Nó sử dụng wsgi.file_wrapper nếu được cung cấp bởi máy chủ wsgi, nếu không nó sẽ truyền tệp ra thành từng phần nhỏ.

import os 
from django.http import FileResponse 
from django.core.servers.basehttp import FileWrapper 


def download_file(request): 
    _file = '/folder/my_file.zip' 
    filename = os.path.basename(_file) 
    response = FileResponse(FileWrapper(file(filename, 'rb')), content_type='application/x-zip-compressed') 
    response['Content-Disposition'] = "attachment; filename=%s" % _file 
    return response 
+1

Để sử dụng django 1.10, từ wsgiref.util nhập FileWrapper –

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