From de4832c49b8a8cf00b2d602ab4d10c4ca69627bd Mon Sep 17 00:00:00 2001 From: ShingenPizza <19253513+ShingenPizza@users.noreply.github.com> Date: Fri, 17 May 2019 12:07:27 +0200 Subject: [PATCH] Fixed #30196 -- Made FileResponse set Content-Disposition inline if filename is available. --- django/http/response.py | 20 +++++++++++--------- docs/ref/request-response.txt | 12 +++++++----- tests/responses/test_fileresponse.py | 9 +++++++++ 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/django/http/response.py b/django/http/response.py index a9ede09dd95..04efbd6bef3 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -436,15 +436,17 @@ class FileResponse(StreamingHttpResponse): else: self['Content-Type'] = 'application/octet-stream' - if self.as_attachment: - filename = self.filename or os.path.basename(filename) - if filename: - try: - filename.encode('ascii') - file_expr = 'filename="{}"'.format(filename) - except UnicodeEncodeError: - file_expr = "filename*=utf-8''{}".format(quote(filename)) - self['Content-Disposition'] = 'attachment; {}'.format(file_expr) + filename = self.filename or os.path.basename(filename) + if filename: + disposition = 'attachment' if self.as_attachment else 'inline' + try: + filename.encode('ascii') + file_expr = 'filename="{}"'.format(filename) + except UnicodeEncodeError: + file_expr = "filename*=utf-8''{}".format(quote(filename)) + self['Content-Disposition'] = '{}; {}'.format(disposition, file_expr) + elif self.as_attachment: + self['Content-Disposition'] = 'attachment' class HttpResponseRedirectBase(HttpResponse): diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt index 58a7c3e7e40..28e5170595e 100644 --- a/docs/ref/request-response.txt +++ b/docs/ref/request-response.txt @@ -1107,15 +1107,17 @@ Attributes optimized for binary files. It uses `wsgi.file_wrapper`_ if provided by the wsgi server, otherwise it streams the file out in small chunks. - If ``as_attachment=True``, the ``Content-Disposition`` header is set, which - asks the browser to offer the file to the user as a download. + If ``as_attachment=True``, the ``Content-Disposition`` header is set to + ``attachment``, which asks the browser to offer the file to the user as a + download. Otherwise, a ``Content-Disposition`` header with a value of + ``inline`` (the browser default) will be set only if a filename is + available. If ``open_file`` doesn't have a name or if the name of ``open_file`` isn't appropriate, provide a custom file name using the ``filename`` parameter. - The ``Content-Length``, ``Content-Type``, and ``Content-Disposition`` - headers are automatically set when they can be guessed from contents of - ``open_file``. + The ``Content-Length`` and ``Content-Type`` headers are automatically set + when they can be guessed from contents of ``open_file``. .. _wsgi.file_wrapper: https://www.python.org/dev/peps/pep-3333/#optional-platform-specific-file-handling diff --git a/tests/responses/test_fileresponse.py b/tests/responses/test_fileresponse.py index 5896373d4dc..e77df4513a2 100644 --- a/tests/responses/test_fileresponse.py +++ b/tests/responses/test_fileresponse.py @@ -14,12 +14,21 @@ class FileResponseTests(SimpleTestCase): response = FileResponse(open(__file__, 'rb')) self.assertEqual(response['Content-Length'], str(os.path.getsize(__file__))) self.assertIn(response['Content-Type'], ['text/x-python', 'text/plain']) + self.assertEqual(response['Content-Disposition'], 'inline; filename="test_fileresponse.py"') response.close() def test_file_from_buffer_response(self): response = FileResponse(io.BytesIO(b'binary content')) self.assertEqual(response['Content-Length'], '14') self.assertEqual(response['Content-Type'], 'application/octet-stream') + self.assertFalse(response.has_header('Content-Disposition')) + self.assertEqual(list(response), [b'binary content']) + + def test_file_from_buffer_unnamed_attachment(self): + response = FileResponse(io.BytesIO(b'binary content'), as_attachment=True) + self.assertEqual(response['Content-Length'], '14') + self.assertEqual(response['Content-Type'], 'application/octet-stream') + self.assertEqual(response['Content-Disposition'], 'attachment') self.assertEqual(list(response), [b'binary content']) @skipIf(sys.platform == 'win32', "Named pipes are Unix-only.")