需求描述
对于一些应用产品,
有播放视频的需求时,
我们往往优先考虑的是通过 Nginx 代理静态资源实现
仅仅需要你在 nginx 配置文件中配置即可达到实现需求
而这么做的劣势在于加载庞大数据的时候,
增加了内存负载
追求完美的我们决不允许这样的事情发生,
所以我们用 API 的方式做流式响应。
依赖环境
Python: 3.7.2
Django: 2.1.7
复制代码
实现源码
#!/usr/bin/python3
# _*_ Coding: UTF-8 _*_
import mime*
import os
import re
from wsgiref.util import FileWrapper
from django.http import StreamingHttpResponse
from rest_framework.views import APIView
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
class StreamVideoPlayViewSet(APIView):
def get(self, request, *args, **kwargs):
""" 将视频文件以 流媒体 的方式响应 """
path = r'这是你动态构建的媒体文件路径'
range_re = re.compile(r'bytes\s*=\s*(\d+)\s*-\s*(\d*)', re.I)
range_match = range_re.match(request.META.get('HTTP_RANGE', '').strip())
size, (content_type, encoding) = os.path.getsize(path), mime*.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 每片,响应体大体积
if last_byte >= size: last_byte = size - 1
length = last_byte - first_byte + 1
response = StreamingHttpResponse(file_iterator(path, offset=first_byte, length=length), status=206, content_type=content_type)
response['Content-Length'], response['Content-Range'] = str(length), 'bytes %s-%s/%s' % (first_byte, last_byte, size)
else:
# 不是以视频流方式的获取时,以生成器方式返回整个文件,节省内存
response = StreamingHttpResponse(FileWrapper(open(path, 'rb')), content_type=content_type)
response['Content-Length'] = str(size)
response['Accept-Ranges'] = 'bytes'
return response
复制代码