|
@ -0,0 +1,35 @@ |
|
|
|
|
|
import os |
|
|
|
|
|
|
|
|
|
|
|
from django.utils.deprecation import MiddlewareMixin |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RangesMiddleware(MiddlewareMixin): |
|
|
|
|
|
"""Quick solution. See: |
|
|
|
|
|
https://stackoverflow.com/questions/14324250/byte-ranges-in-django/35928017#35928017 |
|
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def process_response(self, request, response): |
|
|
|
|
|
if response.status_code != 200 or not hasattr(response, 'file_to_stream'): |
|
|
|
|
|
return response |
|
|
|
|
|
http_range = request.META.get('HTTP_RANGE') |
|
|
|
|
|
if not (http_range and http_range.startswith('bytes=') and http_range.count('-') == 1): |
|
|
|
|
|
return response |
|
|
|
|
|
if_range = request.META.get('HTTP_IF_RANGE') |
|
|
|
|
|
if if_range and if_range != response.get('Last-Modified') and if_range != response.get('ETag'): |
|
|
|
|
|
return response |
|
|
|
|
|
f = response.file_to_stream |
|
|
|
|
|
statobj = os.fstat(f.fileno()) |
|
|
|
|
|
start, end = http_range.split('=')[1].split('-') |
|
|
|
|
|
if not start: # requesting the last N bytes |
|
|
|
|
|
start = max(0, statobj.st_size - int(end)) |
|
|
|
|
|
end = '' |
|
|
|
|
|
start, end = int(start or 0), int(end or statobj.st_size - 1) |
|
|
|
|
|
assert 0 <= start < statobj.st_size, (start, statobj.st_size) |
|
|
|
|
|
end = min(end, statobj.st_size - 1) |
|
|
|
|
|
f.seek(start) |
|
|
|
|
|
old_read = f.read |
|
|
|
|
|
f.read = lambda n: old_read(min(n, end + 1 - f.tell())) |
|
|
|
|
|
response.status_code = 206 |
|
|
|
|
|
response['Content-Length'] = end + 1 - start |
|
|
|
|
|
response['Content-Range'] = 'bytes %d-%d/%d' % (start, end, statobj.st_size) |
|
|
|
|
|
return response |