13👍
The are two relevant feature requests (one is open, another is a duplicate of the first):
- Ticket #22479 – Support byte range requests in django.views.static.serve
- Ticket #23382 – Support byte range requests in django.views.static.serve
Both of the issues are based on the Google Group discussion.
The ticket is in a “hanging” state due to architectural concerns and since there is no consensus on whether this is really something that Django should support. Mostly because web-servers are capable of byte serving.
If you are still interested in the implementation, there is a not-yet-reviewed patch sent implementing a special RangedFileReader
class for returning the response in chunks using StreamingHttpResponse
, there is parsing and sanity checking for HTTP_RANGE
header:
You can try out the fork or use the solution as a base of your own.
FYI, there was an another attempt made here – it was not finished, but can be useful to review.
To parse Range
header, see:
werkzeug
‘sparse_range_header()
parse_range_header()
from the mentioned above PRhttpheader
‘sparse_range_header()
(see also Using HTTP range requests)
13👍
Here’s some basic middleware code that should work for Django 1.11+. It only handles a single range, but that’s all I personally need.
import os
from django.utils.deprecation import MiddlewareMixin
class RangesMiddleware(MiddlewareMixin):
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
Install it in settings.py
like so:
MIDDLEWARE_CLASSES = [
'path.to.RangesMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
]
- [Django]-How can i pass data to django layouts (like 'base.html') without having to provide it through every view?
- [Django]-Django – How to pass several arguments to the url template tag
- [Django]-Django composite unique on multiple model fields
3👍
Three approches about content range
Choose proper one by your situation.
1. Support all dynamic views and static files
The WhiteNoise provides a middleware to support global settings.
2. Support static files only
Nginx can support static and media files.
See more details on Setting up Django and your web server with uWSGI and nginx. If some views need redirect to static or media files, see the stackoverflow answer.
3. Support specific dynamic views only
Django Ranged Response supports responses with content range. Use this response for every views you wanted. The views are mostly designed for media API, like speech synthesis, video generators.
declare: I am a contributer of Django Ranged Response. But I think using WhiteNoise is easiest to maintain.
- [Django]-Django: best practice way to get model from an instance of that model
- [Django]-Django Admin's "view on site" points to example.com instead of my domain
- [Django]-How to debug Jinja2 template?
0👍
If you just need this in testing and don’t mind running nginx
in front of your django
(which has plenty of other advantages, like chunking, etc.), then you can add proxy_force_ranges on;
to http/server/location, which, for example, fixes the issues like Chrome requiring this to be able to seek in videos (by setting video.currentTime
), etc.
- [Django]-Django model CharField: max_length does not work?
- [Django]-Integrate django password validators with django rest framework validate_password
- [Django]-How do I change the range of the x-axis with datetime?