You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

35 lines
1.5 KiB

  1. import os
  2. from django.utils.deprecation import MiddlewareMixin
  3. class RangesMiddleware(MiddlewareMixin):
  4. """Quick solution. See:
  5. https://stackoverflow.com/questions/14324250/byte-ranges-in-django/35928017#35928017
  6. """
  7. def process_response(self, request, response):
  8. if response.status_code != 200 or not hasattr(response, 'file_to_stream'):
  9. return response
  10. http_range = request.META.get('HTTP_RANGE')
  11. if not (http_range and http_range.startswith('bytes=') and http_range.count('-') == 1):
  12. return response
  13. if_range = request.META.get('HTTP_IF_RANGE')
  14. if if_range and if_range != response.get('Last-Modified') and if_range != response.get('ETag'):
  15. return response
  16. f = response.file_to_stream
  17. statobj = os.fstat(f.fileno())
  18. start, end = http_range.split('=')[1].split('-')
  19. if not start: # requesting the last N bytes
  20. start = max(0, statobj.st_size - int(end))
  21. end = ''
  22. start, end = int(start or 0), int(end or statobj.st_size - 1)
  23. assert 0 <= start < statobj.st_size, (start, statobj.st_size)
  24. end = min(end, statobj.st_size - 1)
  25. f.seek(start)
  26. old_read = f.read
  27. f.read = lambda n: old_read(min(n, end + 1 - f.tell()))
  28. response.status_code = 206
  29. response['Content-Length'] = end + 1 - start
  30. response['Content-Range'] = 'bytes %d-%d/%d' % (start, end, statobj.st_size)
  31. return response