Added streaming sendfile backend

The simple sendfile backend doesn't stream responses. This commit adds a backend that does.
This commit is contained in:
Karl Hobley 2015-04-23 12:33:29 +01:00
parent b3348534cc
commit f70eb09d07
2 changed files with 66 additions and 4 deletions

View file

@ -0,0 +1,63 @@
# Sendfile "streaming" backend
# This is based on sendfiles builtin "simple" backend but uses a StreamingHttpResponse
import os
import stat
import re
try:
from email.utils import parsedate_tz, mktime_tz
except ImportError:
from email.Utils import parsedate_tz, mktime_tz
from wsgiref.util import FileWrapper
from django.core.files.base import File
from django.http import StreamingHttpResponse, HttpResponseNotModified
from django.utils.http import http_date
def sendfile(request, filename, **kwargs):
# Respect the If-Modified-Since header.
statobj = os.stat(filename)
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
return HttpResponseNotModified()
response = StreamingHttpResponse(FileWrapper(open(filename, 'rb')))
response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
return response
def was_modified_since(header=None, mtime=0, size=0):
"""
Was something modified since the user last downloaded it?
header
This is the value of the If-Modified-Since header. If this is None,
I'll just return True.
mtime
This is the modification time of the item we're talking about.
size
This is the size of the item we're talking about.
"""
try:
if header is None:
raise ValueError
matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header,
re.IGNORECASE)
header_date = parsedate_tz(matches.group(1))
if header_date is None:
raise ValueError
header_mtime = mktime_tz(header_date)
header_len = matches.group(3)
if header_len and int(header_len) != size:
raise ValueError
if mtime > header_mtime:
raise ValueError
except (AttributeError, ValueError, OverflowError):
return True
return False

View file

@ -2,6 +2,7 @@ from django.shortcuts import get_object_or_404
from django.conf import settings
from wagtail.utils.sendfile import sendfile
from wagtail.utils import sendfile_streaming_backend
from wagtail.wagtaildocs.models import Document, document_served
@ -15,7 +16,5 @@ def serve(request, document_id, document_filename):
if hasattr(settings, 'SENDFILE_BACKEND'):
return sendfile(request, doc.file.path, attachment=True, attachment_filename=doc.filename)
else:
# Fallback to simple backend if user hasn't specified SENDFILE_BACKEND (will crash by default)
from sendfile.backends.simple import sendfile as simple_sendfile_backend
return sendfile(request, doc.file.path, attachment=True, attachment_filename=doc.filename, backend=simple_sendfile_backend)
# Fallback to streaming backend if user hasn't specified SENDFILE_BACKEND
return sendfile(request, doc.file.path, attachment=True, attachment_filename=doc.filename, backend=sendfile_streaming_backend.sendfile)