From 7f79b124c6153a200116906a72110069f0eac268 Mon Sep 17 00:00:00 2001 From: David Fischer Date: Sun, 13 Apr 2014 09:22:32 +0200 Subject: [PATCH 1/8] Convert to bytes and not str --- sendfile/backends/nginx.py | 4 ++-- sendfile/tests.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sendfile/backends/nginx.py b/sendfile/backends/nginx.py index 64b38d7..ca103f8 100644 --- a/sendfile/backends/nginx.py +++ b/sendfile/backends/nginx.py @@ -1,7 +1,7 @@ from __future__ import absolute_import from django.http import HttpResponse -from django.utils.encoding import smart_str +from django.utils.encoding import smart_bytes from ._internalredirect import _convert_file_to_url @@ -9,6 +9,6 @@ def sendfile(request, filename, **kwargs): response = HttpResponse() url = _convert_file_to_url(filename) - response['X-Accel-Redirect'] = smart_str(url) + response['X-Accel-Redirect'] = smart_bytes(url) return response diff --git a/sendfile/tests.py b/sendfile/tests.py index d4a18b1..06821d8 100644 --- a/sendfile/tests.py +++ b/sendfile/tests.py @@ -3,7 +3,7 @@ from django.conf import settings from django.test import TestCase from django.http import HttpResponse, Http404, HttpRequest -from django.utils.encoding import smart_str +from django.utils.encoding import smart_bytes, smart_str import os.path from tempfile import mkdtemp import shutil @@ -105,7 +105,7 @@ def test_xsendfile_header_containing_unicode(self): filepath = self.ensure_file(u'péter_là_gueule.txt') response = real_sendfile(HttpRequest(), filepath) self.assertTrue(response is not None) - self.assertEqual(smart_str(filepath), response['X-Sendfile']) + self.assertEqual(smart_bytes(filepath), response['X-Sendfile']) class TestNginxBackend(TempFileTestCase): From 74aef197a6408f59fb97b8cebd0c4914781418ba Mon Sep 17 00:00:00 2001 From: David Fischer Date: Mon, 25 Dec 2017 11:26:57 +0100 Subject: [PATCH 2/8] Fix PEP-8 issues --- sendfile/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/sendfile/__init__.py b/sendfile/__init__.py index 1336112..bf3884f 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -1,16 +1,18 @@ -VERSION = (0, 3, 4) -__version__ = '.'.join(map(str, VERSION)) - import os.path from mimetypes import guess_type +VERSION = (0, 3, 4) +__version__ = '.'.join(map(str, VERSION)) + def _lazy_load(fn): _cached = [] + def _decorated(): if not _cached: _cached.append(fn()) return _cached[0] + def clear(): while _cached: _cached.pop() @@ -31,7 +33,6 @@ def _get_sendfile(): return module.sendfile - def sendfile(request, filename, attachment=False, attachment_filename=None, mimetype=None, encoding=None): ''' create a response to send file using backend configured in SENDFILE_BACKEND @@ -60,7 +61,7 @@ def sendfile(request, filename, attachment=False, attachment_filename=None, mime mimetype = guessed_mimetype else: mimetype = 'application/octet-stream' - + response = _sendfile(request, filename, mimetype=mimetype) if attachment: if attachment_filename is None: From 13efddf5b72188b4f3ff53485d28197a43512acf Mon Sep 17 00:00:00 2001 From: David Fischer Date: Mon, 25 Dec 2017 11:39:22 +0100 Subject: [PATCH 3/8] Add check_exist parameter --- sendfile/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sendfile/__init__.py b/sendfile/__init__.py index bf3884f..014a16b 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -33,7 +33,8 @@ def _get_sendfile(): return module.sendfile -def sendfile(request, filename, attachment=False, attachment_filename=None, mimetype=None, encoding=None): +def sendfile(request, filename, check_exist=False, attachment=False, attachment_filename=None, + mimetype=None, encoding=None): ''' create a response to send file using backend configured in SENDFILE_BACKEND @@ -51,7 +52,7 @@ def sendfile(request, filename, attachment=False, attachment_filename=None, mime ''' _sendfile = _get_sendfile() - if not os.path.exists(filename): + if check_exist and not os.path.exists(filename): from django.http import Http404 raise Http404('"%s" does not exist' % filename) From b838474b59fb04385897c4e58079f48e8250b868 Mon Sep 17 00:00:00 2001 From: David Fischer Date: Mon, 25 Dec 2017 11:39:59 +0100 Subject: [PATCH 4/8] Fix PEP-8 issues --- sendfile/__init__.py | 6 +++--- sendfile/tests.py | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/sendfile/__init__.py b/sendfile/__init__.py index 014a16b..bb84fbe 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -35,8 +35,8 @@ def _get_sendfile(): def sendfile(request, filename, check_exist=False, attachment=False, attachment_filename=None, mimetype=None, encoding=None): - ''' - create a response to send file using backend configured in SENDFILE_BACKEND + """ + Create a response to send file using backend configured in SENDFILE_BACKEND. If attachment is True the content-disposition header will be set. This will typically prompt the user to download the file, rather @@ -49,7 +49,7 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ If no mimetype or encoding are specified, then they will be guessed via the filename (using the standard python mimetypes module) - ''' + """ _sendfile = _get_sendfile() if check_exist and not os.path.exists(filename): diff --git a/sendfile/tests.py b/sendfile/tests.py index 06821d8..3bf738e 100644 --- a/sendfile/tests.py +++ b/sendfile/tests.py @@ -85,7 +85,9 @@ def test_attachment_filename(self): def test_attachment_filename_unicode(self): response = real_sendfile(HttpRequest(), self._get_readme(), attachment=True, attachment_filename='test’s.txt') self.assertTrue(response is not None) - self.assertEqual('attachment; filename="test\'s.txt"; filename*=UTF-8\'\'test%E2%80%99s.txt', response['Content-Disposition']) + self.assertEqual( + 'attachment; filename="test\'s.txt"; filename*=UTF-8\'\'test%E2%80%99s.txt', + response['Content-Disposition']) class TestXSendfileBackend(TempFileTestCase): From 71b22ea9421cb854bf2869d4bf37e10a7673d750 Mon Sep 17 00:00:00 2001 From: David Fischer Date: Mon, 25 Dec 2017 11:40:29 +0100 Subject: [PATCH 5/8] Avoid I/O ops when not necessary --- sendfile/__init__.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sendfile/__init__.py b/sendfile/__init__.py index bb84fbe..87a20ff 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -47,8 +47,10 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ False: No content-disposition filename String: Value used as filename - If no mimetype or encoding are specified, then they will be guessed via the - filename (using the standard python mimetypes module) + If mimetype is set to None then it will be automatically guessed + via the filename (using the standard python mimetypes module). + + The same apply for encoding. """ _sendfile = _get_sendfile() @@ -56,12 +58,12 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ from django.http import Http404 raise Http404('"%s" does not exist' % filename) - guessed_mimetype, guessed_encoding = guess_type(filename) - if mimetype is None: - if guessed_mimetype: - mimetype = guessed_mimetype - else: - mimetype = 'application/octet-stream' + if mimetype is None or encoding is None: + guessed_mimetype, guessed_encoding = guess_type(filename) + if mimetype is None: + mimetype = guessed_mimetype or 'application/octet-stream' + if encoding is None: + encoding = guessed_encoding response = _sendfile(request, filename, mimetype=mimetype) if attachment: @@ -86,8 +88,6 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ response['Content-length'] = os.path.getsize(filename) response['Content-Type'] = mimetype - if not encoding: - encoding = guessed_encoding if encoding: response['Content-Encoding'] = encoding From ad6c8547d2b72e22c4f6752f5025e3daaf5175c9 Mon Sep 17 00:00:00 2001 From: David Fischer Date: Mon, 25 Dec 2017 12:00:30 +0100 Subject: [PATCH 6/8] Make Content headers optional: Web Engine can also manage them! --- sendfile/__init__.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/sendfile/__init__.py b/sendfile/__init__.py index 87a20ff..7c8fc45 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -4,6 +4,8 @@ VERSION = (0, 3, 4) __version__ = '.'.join(map(str, VERSION)) +_guess = object() + def _lazy_load(fn): _cached = [] @@ -34,7 +36,7 @@ def _get_sendfile(): def sendfile(request, filename, check_exist=False, attachment=False, attachment_filename=None, - mimetype=None, encoding=None): + encoding=_guess, filesize=_guess, mimetype=_guess): """ Create a response to send file using backend configured in SENDFILE_BACKEND. @@ -47,10 +49,12 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ False: No content-disposition filename String: Value used as filename - If mimetype is set to None then it will be automatically guessed - via the filename (using the standard python mimetypes module). + Any of encoding, filesize and mimetype left to _guess will be + guessed via the filename (using the standard python mimetypes + and os.path modules). - The same apply for encoding. + Any of encoding, filesize and mimetype set to None will not + be set into the response headers. """ _sendfile = _get_sendfile() @@ -58,11 +62,13 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ from django.http import Http404 raise Http404('"%s" does not exist' % filename) - if mimetype is None or encoding is None: + if filesize is _guess: + filesize = os.path.getsize(filename) + if mimetype is _guess or encoding is _guess: guessed_mimetype, guessed_encoding = guess_type(filename) - if mimetype is None: + if mimetype is _guess: mimetype = guessed_mimetype or 'application/octet-stream' - if encoding is None: + if encoding is _guess: encoding = guessed_encoding response = _sendfile(request, filename, mimetype=mimetype) @@ -86,9 +92,11 @@ def sendfile(request, filename, check_exist=False, attachment=False, attachment_ parts.append('filename*=UTF-8\'\'%s' % quoted_filename) response['Content-Disposition'] = '; '.join(parts) - response['Content-length'] = os.path.getsize(filename) - response['Content-Type'] = mimetype - if encoding: + if encoding is not None: response['Content-Encoding'] = encoding + if filesize is not None: + response['Content-Length'] = filesize + if mimetype is not None: + response['Content-Type'] = mimetype return response From 18db2084c02f2389a7a32811f564ebdff3f89322 Mon Sep 17 00:00:00 2001 From: David Fischer Date: Sat, 30 Dec 2017 16:52:32 +0100 Subject: [PATCH 7/8] Fix import of import_module on Django >= 1.9 --- sendfile/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sendfile/__init__.py b/sendfile/__init__.py index 7c8fc45..12d216c 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -24,7 +24,10 @@ def clear(): @_lazy_load def _get_sendfile(): - from django.utils.importlib import import_module + try: + from django.utils.importlib import import_module + except ImportError: # Django >= 1.9 + from importlib import import_module from django.conf import settings from django.core.exceptions import ImproperlyConfigured From 82c3d550d5b541661cbccbcdba1597e348309a25 Mon Sep 17 00:00:00 2001 From: David Fischer Date: Wed, 21 Jun 2023 00:10:18 +0200 Subject: [PATCH 8/8] Update README.rst --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 76ccf99..1be3897 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,8 @@ Django Sendfile =============== +Please use https://github.com/davidfischer-ch/django-sendfile2 instead. + This is a wrapper around web-server specific methods for sending files to web clients. This is useful when Django needs to check permissions associated files, but does not want to serve the actual bytes of the file itself. i.e. as serving large files is not what Django is made for. Note this should not be used for regular file serving (e.g. css etc), only for cases where you need Django to do some work before serving the actual file.