diff options
| author | Piotr Kunicki <piotr.kunicki@o2.pl> | 2021-10-14 11:41:10 +0200 |
|---|---|---|
| committer | Carlton Gibson <carlton.gibson@noumenal.es> | 2021-10-14 15:05:04 +0200 |
| commit | dc724c5bf9d3b8d59c9571aa751c3cd001cdeced (patch) | |
| tree | 8f3d1079a843438a6a2f95a40a30a1528259ec5d /django/http | |
| parent | 3ac476439751334138edcbb9abd73f783afcec4c (diff) | |
Fixed #30509 -- Made FileResponse better handle buffers and non-zero file offsets.
Diffstat (limited to 'django/http')
| -rw-r--r-- | django/http/response.py | 39 |
1 files changed, 25 insertions, 14 deletions
diff --git a/django/http/response.py b/django/http/response.py index ab7158f7e0..68b7a8f285 100644 --- a/django/http/response.py +++ b/django/http/response.py @@ -1,4 +1,5 @@ import datetime +import io import json import mimetypes import os @@ -437,6 +438,7 @@ class FileResponse(StreamingHttpResponse): def __init__(self, *args, as_attachment=False, filename='', **kwargs): self.as_attachment = as_attachment self.filename = filename + self._no_default_content_type_set = 'content_type' not in kwargs or kwargs['content_type'] is None super().__init__(*args, **kwargs) def _set_streaming_content(self, value): @@ -456,29 +458,38 @@ class FileResponse(StreamingHttpResponse): Set some common response headers (Content-Length, Content-Type, and Content-Disposition) based on the `filelike` response content. """ - encoding_map = { - 'bzip2': 'application/x-bzip', - 'gzip': 'application/gzip', - 'xz': 'application/x-xz', - } - filename = getattr(filelike, 'name', None) - filename = filename if (isinstance(filename, str) and filename) else self.filename - if os.path.isabs(filename): - self.headers['Content-Length'] = os.path.getsize(filelike.name) - elif hasattr(filelike, 'getbuffer'): - self.headers['Content-Length'] = filelike.getbuffer().nbytes + filename = getattr(filelike, 'name', '') + filename = filename if isinstance(filename, str) else '' + seekable = hasattr(filelike, 'seek') and (not hasattr(filelike, 'seekable') or filelike.seekable()) + if hasattr(filelike, 'tell'): + if seekable: + initial_position = filelike.tell() + filelike.seek(0, io.SEEK_END) + self.headers['Content-Length'] = filelike.tell() - initial_position + filelike.seek(initial_position) + elif hasattr(filelike, 'getbuffer'): + self.headers['Content-Length'] = filelike.getbuffer().nbytes - filelike.tell() + elif os.path.exists(filename): + self.headers['Content-Length'] = os.path.getsize(filename) - filelike.tell() + elif seekable: + self.headers['Content-Length'] = sum(iter(lambda: len(filelike.read(self.block_size)), 0)) + filelike.seek(-int(self.headers['Content-Length']), io.SEEK_END) - if self.headers.get('Content-Type', '').startswith('text/html'): + filename = os.path.basename(self.filename or filename) + if self._no_default_content_type_set: if filename: content_type, encoding = mimetypes.guess_type(filename) # Encoding isn't set to prevent browsers from automatically # uncompressing files. - content_type = encoding_map.get(encoding, content_type) + content_type = { + 'bzip2': 'application/x-bzip', + 'gzip': 'application/gzip', + 'xz': 'application/x-xz', + }.get(encoding, content_type) self.headers['Content-Type'] = content_type or 'application/octet-stream' else: self.headers['Content-Type'] = 'application/octet-stream' - filename = self.filename or os.path.basename(filename) if filename: disposition = 'attachment' if self.as_attachment else 'inline' try: |
