123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132 |
- """
- 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 _
- from django.utils.translation import 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
-
- path('<path: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
- ):
- 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.headers["Last-Modified"] = http_date(statobj.st_mtime)
- if encoding:
- response.headers["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>{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}</title>
- </head>
- <body>
- <h1>{% blocktranslate %}Index of {{ directory }}{% endblocktranslate %}</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):
- """
- 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.
- """
- try:
- if header is None:
- raise ValueError
- header_mtime = parse_http_date(header)
- if int(mtime) > header_mtime:
- raise ValueError
- except (ValueError, OverflowError):
- return True
- return False
|