123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- """
- 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
- import re
- 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
-
-
- def 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
-
- url(r'^(?P<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'
- response = FileResponse(fullpath.open('rb'), content_type=content_type)
- response["Last-Modified"] = http_date(statobj.st_mtime)
- if encoding:
- response["Content-Encoding"] = encoding
- return response
-
-
- DEFAULT_DIRECTORY_INDEX_TEMPLATE = """
- {% load i18n %}
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8">
- <meta http-equiv="Content-Language" content="en-us">
- <meta name="robots" content="NONE,NOARCHIVE">
- <title>{% blocktrans %}Index of {{ directory }}{% endblocktrans %}</title>
- </head>
- <body>
- <h1>{% blocktrans %}Index of {{ directory }}{% endblocktrans %}</h1>
- <ul>
- {% if directory != "/" %}
- <li><a href="../">../</a></li>
- {% endif %}
- {% for f in file_list %}
- <li><a href="{{ f|urlencode }}">{{ f }}</a></li>
- {% endfor %}
- </ul>
- </body>
- </html>
- """
- 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.group(1))
- header_len = matches.group(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
|