""" Views and functions for serving static files. These are only to be used during development, and SHOULD NOT be used in a production setting. """ """ 流媒体文件的流式响应(支持视频快进) """ import mimetypes import posixpath from pathlib import Path from django.http import ( FileResponse, Http404, HttpResponse, HttpResponseNotModified, ) from django.template import Context, Engine, TemplateDoesNotExist, loader from django.utils._os import safe_join from django.utils.http import http_date, parse_http_date from django.utils.translation import gettext as _, gettext_lazy import re import os from wsgiref.util import FileWrapper from django.http import StreamingHttpResponse def streamingmedia_serve(request, path, document_root=None, show_indexes=False): """ Serve static files below a given point in the directory structure. To use, put a URL pattern such as:: from django.views.static import serve path('', serve, {'document_root': '/path/to/my/files/'}) in your URLconf. You must provide the ``document_root`` param. You may also set ``show_indexes`` to ``True`` if you'd like to serve a basic index of the directory. This index view will use the template hardcoded below, but if you'd like to override it, you can create a template called ``static/directory_index.html``. """ path = posixpath.normpath(path).lstrip('/') fullpath = Path(safe_join(document_root, path)) if fullpath.is_dir(): if show_indexes: return directory_index(path, fullpath) raise Http404(_("Directory indexes are not allowed here.")) if not fullpath.exists(): raise Http404(_('“%(path)s” does not exist') % {'path': fullpath}) # Respect the If-Modified-Since header. statobj = fullpath.stat() if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), statobj.st_mtime, statobj.st_size): return HttpResponseNotModified() content_type, encoding = mimetypes.guess_type(str(fullpath)) content_type = content_type or 'application/octet-stream' if content_type in ['video/mp4','video/ogg', 'video/flv', 'video/avi', 'video/wmv', 'video/rmvb','audio/mp3','audio/x-m4a','audio/mpeg','audio/ogg']: response = stream_video(request, fullpath) return response elif fullpath.suffix in ['.apk']: # 兼容小米的浏览器等迅雷下载内核无法下载问题 content_type = 'application/vnd.android.package-archive' response = FileResponse(fullpath.open('rb'), content_type=content_type) response['Content-Length'] = fullpath.stat().st_size return response else: response = FileResponse(fullpath.open('rb'), content_type=content_type) response.headers["Last-Modified"] = http_date(statobj.st_mtime) if encoding: response.headers["Content-Encoding"] = encoding return response DEFAULT_DIRECTORY_INDEX_TEMPLATE = """ {% load i18n %} {% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}

{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}

""" template_translatable = gettext_lazy("Index of %(directory)s") def directory_index(path, fullpath): try: t = loader.select_template([ 'static/directory_index.html', 'static/directory_index', ]) except TemplateDoesNotExist: t = Engine(libraries={'i18n': 'django.templatetags.i18n'}).from_string(DEFAULT_DIRECTORY_INDEX_TEMPLATE) c = Context() else: c = {} files = [] for f in fullpath.iterdir(): if not f.name.startswith('.'): url = str(f.relative_to(fullpath)) if f.is_dir(): url += '/' files.append(url) c.update({ 'directory': path + '/', 'file_list': files, }) return HttpResponse(t.render(c)) def was_modified_since(header=None, mtime=0, size=0): """ Was something modified since the user last downloaded it? header This is the value of the If-Modified-Since header. If this is None, I'll just return True. mtime This is the modification time of the item we're talking about. size This is the size of the item we're talking about. """ try: if header is None: raise ValueError matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header, re.IGNORECASE) header_mtime = parse_http_date(matches[1]) header_len = matches[3] if header_len and int(header_len) != size: raise ValueError if int(mtime) > header_mtime: raise ValueError except (AttributeError, ValueError, OverflowError): return True return False 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): """将视频文件以流媒体的方式响应""" 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) #这里根据实际情况改变,我的views.py在core文件夹下但是folder_path却只到core的上一层,media也在core文件夹下 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 * 10 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: resp = StreamingHttpResponse(FileWrapper(open(path, 'rb')), content_type=content_type) resp['Content-Length'] = str(size) resp['Accept-Ranges'] = 'bytes' return resp