mirror of
https://github.com/django/django.git
synced 2024-12-23 01:25:58 +00:00
Fixed #34194 -- Added django.utils.http.content_disposition_header().
This commit is contained in:
parent
3d3e955efa
commit
cbce427c17
@ -8,7 +8,7 @@ import sys
|
|||||||
import time
|
import time
|
||||||
from email.header import Header
|
from email.header import Header
|
||||||
from http.client import responses
|
from http.client import responses
|
||||||
from urllib.parse import quote, urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.core import signals, signing
|
from django.core import signals, signing
|
||||||
@ -18,7 +18,7 @@ from django.http.cookie import SimpleCookie
|
|||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.datastructures import CaseInsensitiveMapping
|
from django.utils.datastructures import CaseInsensitiveMapping
|
||||||
from django.utils.encoding import iri_to_uri
|
from django.utils.encoding import iri_to_uri
|
||||||
from django.utils.http import http_date
|
from django.utils.http import content_disposition_header, http_date
|
||||||
from django.utils.regex_helper import _lazy_re_compile
|
from django.utils.regex_helper import _lazy_re_compile
|
||||||
|
|
||||||
_charset_from_content_type_re = _lazy_re_compile(
|
_charset_from_content_type_re = _lazy_re_compile(
|
||||||
@ -569,20 +569,10 @@ class FileResponse(StreamingHttpResponse):
|
|||||||
else:
|
else:
|
||||||
self.headers["Content-Type"] = "application/octet-stream"
|
self.headers["Content-Type"] = "application/octet-stream"
|
||||||
|
|
||||||
if filename:
|
if content_disposition := content_disposition_header(
|
||||||
disposition = "attachment" if self.as_attachment else "inline"
|
self.as_attachment, filename
|
||||||
try:
|
):
|
||||||
filename.encode("ascii")
|
self.headers["Content-Disposition"] = content_disposition
|
||||||
file_expr = 'filename="{}"'.format(
|
|
||||||
filename.replace("\\", "\\\\").replace('"', r"\"")
|
|
||||||
)
|
|
||||||
except UnicodeEncodeError:
|
|
||||||
file_expr = "filename*=utf-8''{}".format(quote(filename))
|
|
||||||
self.headers["Content-Disposition"] = "{}; {}".format(
|
|
||||||
disposition, file_expr
|
|
||||||
)
|
|
||||||
elif self.as_attachment:
|
|
||||||
self.headers["Content-Disposition"] = "attachment"
|
|
||||||
|
|
||||||
|
|
||||||
class HttpResponseRedirectBase(HttpResponse):
|
class HttpResponseRedirectBase(HttpResponse):
|
||||||
|
@ -10,6 +10,7 @@ from urllib.parse import (
|
|||||||
_coerce_args,
|
_coerce_args,
|
||||||
_splitnetloc,
|
_splitnetloc,
|
||||||
_splitparams,
|
_splitparams,
|
||||||
|
quote,
|
||||||
scheme_chars,
|
scheme_chars,
|
||||||
unquote,
|
unquote,
|
||||||
)
|
)
|
||||||
@ -425,3 +426,24 @@ def parse_header_parameters(line):
|
|||||||
value = unquote(value, encoding=encoding)
|
value = unquote(value, encoding=encoding)
|
||||||
pdict[name] = value
|
pdict[name] = value
|
||||||
return key, pdict
|
return key, pdict
|
||||||
|
|
||||||
|
|
||||||
|
def content_disposition_header(as_attachment, filename):
|
||||||
|
"""
|
||||||
|
Construct a Content-Disposition HTTP header value from the given filename
|
||||||
|
as specified by RFC 6266.
|
||||||
|
"""
|
||||||
|
if filename:
|
||||||
|
disposition = "attachment" if as_attachment else "inline"
|
||||||
|
try:
|
||||||
|
filename.encode("ascii")
|
||||||
|
file_expr = 'filename="{}"'.format(
|
||||||
|
filename.replace("\\", "\\\\").replace('"', r"\"")
|
||||||
|
)
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
file_expr = "filename*=utf-8''{}".format(quote(filename))
|
||||||
|
return f"{disposition}; {file_expr}"
|
||||||
|
elif as_attachment:
|
||||||
|
return "attachment"
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
@ -729,6 +729,15 @@ escaping HTML.
|
|||||||
|
|
||||||
Outputs a string in the format ``Wdy, DD Mon YYYY HH:MM:SS GMT``.
|
Outputs a string in the format ``Wdy, DD Mon YYYY HH:MM:SS GMT``.
|
||||||
|
|
||||||
|
.. function:: content_disposition_header(as_attachment, filename)
|
||||||
|
|
||||||
|
.. versionadded:: 4.2
|
||||||
|
|
||||||
|
Constructs a ``Content-Disposition`` HTTP header value from the given
|
||||||
|
``filename`` as specified by :rfc:`6266`. Returns ``None`` if
|
||||||
|
``as_attachment`` is ``False`` and ``filename`` is ``None``, otherwise
|
||||||
|
returns a string suitable for the ``Content-Disposition`` HTTP header.
|
||||||
|
|
||||||
.. function:: base36_to_int(s)
|
.. function:: base36_to_int(s)
|
||||||
|
|
||||||
Converts a base 36 string to an integer.
|
Converts a base 36 string to an integer.
|
||||||
|
@ -321,6 +321,9 @@ Utilities
|
|||||||
documented functions for handling URL redirects. The Django functions were
|
documented functions for handling URL redirects. The Django functions were
|
||||||
not affected.
|
not affected.
|
||||||
|
|
||||||
|
* The new :func:`django.utils.http.content_disposition_header` function returns
|
||||||
|
a ``Content-Disposition`` HTTP header value as specified by :rfc:`6266`.
|
||||||
|
|
||||||
Validators
|
Validators
|
||||||
~~~~~~~~~~
|
~~~~~~~~~~
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ from django.test import SimpleTestCase
|
|||||||
from django.utils.datastructures import MultiValueDict
|
from django.utils.datastructures import MultiValueDict
|
||||||
from django.utils.http import (
|
from django.utils.http import (
|
||||||
base36_to_int,
|
base36_to_int,
|
||||||
|
content_disposition_header,
|
||||||
escape_leading_slashes,
|
escape_leading_slashes,
|
||||||
http_date,
|
http_date,
|
||||||
int_to_base36,
|
int_to_base36,
|
||||||
@ -511,3 +512,28 @@ class ParseHeaderParameterTests(unittest.TestCase):
|
|||||||
for raw_line, expected_title in test_data:
|
for raw_line, expected_title in test_data:
|
||||||
parsed = parse_header_parameters(raw_line)
|
parsed = parse_header_parameters(raw_line)
|
||||||
self.assertEqual(parsed[1]["title"], expected_title)
|
self.assertEqual(parsed[1]["title"], expected_title)
|
||||||
|
|
||||||
|
|
||||||
|
class ContentDispositionHeaderTests(unittest.TestCase):
|
||||||
|
def test_basic(self):
|
||||||
|
tests = (
|
||||||
|
((False, None), None),
|
||||||
|
((False, "example"), 'inline; filename="example"'),
|
||||||
|
((True, None), "attachment"),
|
||||||
|
((True, "example"), 'attachment; filename="example"'),
|
||||||
|
(
|
||||||
|
(True, '"example" file\\name'),
|
||||||
|
'attachment; filename="\\"example\\" file\\\\name"',
|
||||||
|
),
|
||||||
|
((True, "espécimen"), "attachment; filename*=utf-8''esp%C3%A9cimen"),
|
||||||
|
(
|
||||||
|
(True, '"espécimen" filename'),
|
||||||
|
"attachment; filename*=utf-8''%22esp%C3%A9cimen%22%20filename",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
for (is_attachment, filename), expected in tests:
|
||||||
|
with self.subTest(is_attachment=is_attachment, filename=filename):
|
||||||
|
self.assertEqual(
|
||||||
|
content_disposition_header(is_attachment, filename), expected
|
||||||
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user