mirror of
https://github.com/django/django.git
synced 2025-10-29 00:26:07 +00:00
Fixed #12323 and #11582 -- Extended the ability to handle static files. Thanks to all for helping with the original app, the patch, documentation and general support.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@14293 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
@@ -9,6 +9,7 @@ import posixpath
|
||||
import re
|
||||
import stat
|
||||
import urllib
|
||||
import warnings
|
||||
from email.Utils import parsedate_tz, mktime_tz
|
||||
|
||||
from django.template import loader
|
||||
@@ -16,6 +17,10 @@ from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpRespons
|
||||
from django.template import Template, Context, TemplateDoesNotExist
|
||||
from django.utils.http import http_date
|
||||
|
||||
from django.contrib.staticfiles.views import \
|
||||
directory_index, was_modified_since, serve as staticfiles_serve
|
||||
|
||||
|
||||
def serve(request, path, document_root=None, show_indexes=False):
|
||||
"""
|
||||
Serve static files below a given point in the directory structure.
|
||||
@@ -30,111 +35,7 @@ def serve(request, path, document_root=None, show_indexes=False):
|
||||
but if you'd like to override it, you can create a template called
|
||||
``static/directory_index.html``.
|
||||
"""
|
||||
|
||||
# Clean up given path to only allow serving files below document_root.
|
||||
path = posixpath.normpath(urllib.unquote(path))
|
||||
path = path.lstrip('/')
|
||||
newpath = ''
|
||||
for part in path.split('/'):
|
||||
if not part:
|
||||
# Strip empty path components.
|
||||
continue
|
||||
drive, part = os.path.splitdrive(part)
|
||||
head, part = os.path.split(part)
|
||||
if part in (os.curdir, os.pardir):
|
||||
# Strip '.' and '..' in path.
|
||||
continue
|
||||
newpath = os.path.join(newpath, part).replace('\\', '/')
|
||||
if newpath and path != newpath:
|
||||
return HttpResponseRedirect(newpath)
|
||||
fullpath = os.path.join(document_root, newpath)
|
||||
if os.path.isdir(fullpath):
|
||||
if show_indexes:
|
||||
return directory_index(newpath, fullpath)
|
||||
raise Http404("Directory indexes are not allowed here.")
|
||||
if not os.path.exists(fullpath):
|
||||
raise Http404('"%s" does not exist' % fullpath)
|
||||
# Respect the If-Modified-Since header.
|
||||
statobj = os.stat(fullpath)
|
||||
mimetype, encoding = mimetypes.guess_type(fullpath)
|
||||
mimetype = mimetype or 'application/octet-stream'
|
||||
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
|
||||
statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]):
|
||||
return HttpResponseNotModified(mimetype=mimetype)
|
||||
contents = open(fullpath, 'rb').read()
|
||||
response = HttpResponse(contents, mimetype=mimetype)
|
||||
response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
|
||||
response["Content-Length"] = len(contents)
|
||||
if encoding:
|
||||
response["Content-Encoding"] = encoding
|
||||
return response
|
||||
|
||||
DEFAULT_DIRECTORY_INDEX_TEMPLATE = """
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" 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>Index of {{ directory }}</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Index of {{ directory }}</h1>
|
||||
<ul>
|
||||
{% ifnotequal directory "/" %}
|
||||
<li><a href="../">../</a></li>
|
||||
{% endifnotequal %}
|
||||
{% for f in file_list %}
|
||||
<li><a href="{{ f|urlencode }}">{{ f }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
def directory_index(path, fullpath):
|
||||
try:
|
||||
t = loader.select_template(['static/directory_index.html',
|
||||
'static/directory_index'])
|
||||
except TemplateDoesNotExist:
|
||||
t = Template(DEFAULT_DIRECTORY_INDEX_TEMPLATE, name='Default directory index template')
|
||||
files = []
|
||||
for f in os.listdir(fullpath):
|
||||
if not f.startswith('.'):
|
||||
if os.path.isdir(os.path.join(fullpath, f)):
|
||||
f += '/'
|
||||
files.append(f)
|
||||
c = Context({
|
||||
'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 = mktime_tz(parsedate_tz(matches.group(1)))
|
||||
header_len = matches.group(3)
|
||||
if header_len and int(header_len) != size:
|
||||
raise ValueError
|
||||
if mtime > header_mtime:
|
||||
raise ValueError
|
||||
except (AttributeError, ValueError, OverflowError):
|
||||
return True
|
||||
return False
|
||||
warnings.warn("The view at `django.views.static.serve` is deprecated; "
|
||||
"use the path `django.contrib.staticfiles.views.serve` "
|
||||
"instead.", PendingDeprecationWarning)
|
||||
return staticfiles_serve(request, path, document_root, show_indexes)
|
||||
|
||||
Reference in New Issue
Block a user