在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
有没有更好的方法可以在保证安全的同时,减少服务器的负担呢?提前谢谢大家 :)
6 个回答
5
使用FileResponse会更好,它是StreamingHttpResponse的一个子类,专门为二进制文件优化的。如果你的wsgi服务器支持的话,它会使用wsgi.file_wrapper来处理文件;如果不支持,它就会把文件分成小块逐步发送出去。
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
15
你可以使用'sendfile'这个方法,具体的做法可以参考这个回答。
实际上,你需要这样做(复制粘贴):
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
这需要用到mod_xsendfile,这个功能也被nginx和lighty支持。
69
你打开图片的时候,它会被加载到内存里,这就是在大量使用时导致负载增加的原因。正如马丁所说,真正的解决办法是直接提供文件。
这里还有另一种方法,可以分块传输你的文件,而不需要把它全部加载到内存里。
import os
import mimetypes
from wsgiref.util import FileWrapper
from django.http import StreamingHttpResponse
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"] = f"attachment; filename={filename}"
return response