<video> tag of html5 to play:
<video width="320" height="240" controls> <source src="/static/video/demo.mp4" type="video/mp4"> Your browser does not support the Video tag. </video>
But in this way, the progress bar in the video cannot be used, and if it is returned as a static file, the background program will take up a lot of memory.
The use of response streams can solve these two problems.
Most Django responses use
HttpResponse. This means that the body of the response is built in memory and sent to the HTTP client as a single piece. If you use
StreamingHttpResponse, you can return it in chunks (partial chunks). A very simple example is:
from django.http import StreamingHttpResponse def hello(): yield 'Hello,' yield 'there!' def test(request): return StreamingHttpResponse(hello)
According to the WSGI protocol, when the server is called, the application object must return an iterable, resulting in zero or more byte strings. Therefore, we can complete the flow response function by providing a generator to the server.
The common use of
StreamingHttpResponse is the downloading of some large files, etc., which can also be used to complete the function of resumable upload.
When using a video stream, you can get the starting number of bytes from the request header.
This field seems to be automatically provided by the browser, because in the html code, I only need to change the
src of the video from a static address to a routing method. For the response body, a range of blocks returned by the response body should also be provided:
Content-Rangerespectively represents the start byte number-the end byte number / the total bytes of the file, and the content of the response body contains the content within the range of the file. The code to process the video stream is as follows:
import re import os from wsgiref.util import FileWrapper from django.http import StreamingHttpResponse def file_iterator(file_name, chunk_size=8192, offset=0, length=None): with open(file_name, "rb") as f: f.seek(offset, os.SEEK_SET) remaining = length while True: bytes_length = chunk_size if remaining is None else min(remaining, chunk_size) data = f.read(bytes_length) if not data: break if remaining: remaining -= len(data) yield data def stream_video(request, path): """Responding to the video file by streaming media""" range_header = request.META.get('HTTP_RANGE', '').strip() range_re = re.compile(r'bytes\s*=\s*(\d+)\s*-\s*(\d*)', re.I) range_match = range_re.match(range_header) size = os.path.getsize(path) content_type, encoding = mimetypes.guess_type(path) content_type = content_type or 'application/octet-stream' if range_match: first_byte, last_byte = range_match.groups() first_byte = int(first_byte) if first_byte else 0 last_byte = first_byte + 1024 * 1024 * 8 # 8M Each piece, the maximum volume of the response body if last_byte >= size: last_byte = size - 1 length = last_byte - first_byte + 1 resp = StreamingHttpResponse(file_iterator(path, offset=first_byte, length=length), status=206, content_type=content_type) resp['Content-Length'] = str(length) resp['Content-Range'] = 'bytes %s-%s/%s' % (first_byte, last_byte, size) else: # When it is not obtained by video stream, the entire file is returned by generator to save memory resp = StreamingHttpResponse(FileWrapper(open(path, 'rb')), content_type=content_type) resp['Content-Length'] = str(size) resp['Accept-Ranges'] = 'bytes' return resp