Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
58 changes: 34 additions & 24 deletions sendfile/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
VERSION = (0, 3, 11)
__version__ = '.'.join(map(str, VERSION))

import os.path
from mimetypes import guess_type
import unicodedata

VERSION = (0, 3, 11)
__version__ = '.'.join(map(str, VERSION))

_guess = object()


def _lazy_load(fn):
_cached = []

def _decorated():
if not _cached:
_cached.append(fn())
return _cached[0]

def clear():
while _cached:
_cached.pop()
Expand All @@ -23,7 +27,7 @@ def clear():
def _get_sendfile():
try:
from importlib import import_module
except ImportError:
except ImportError: # Django < 1.9
from django.utils.importlib import import_module
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
Expand All @@ -35,10 +39,10 @@ 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
def sendfile(request, filename, check_exist=False, attachment=False, attachment_filename=None,
encoding=_guess, filesize=_guess, mimetype=_guess):
"""
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
Expand All @@ -49,22 +53,28 @@ def sendfile(request, filename, attachment=False, attachment_filename=None, mime
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)
'''
Any of encoding, filesize and mimetype left to _guess will be
guessed via the filename (using the standard python mimetypes
and os.path modules).

Any of encoding, filesize and mimetype set to None will not
be set into the response headers.
"""
_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)

guessed_mimetype, guessed_encoding = guess_type(filename)
if mimetype is None:
if guessed_mimetype:
mimetype = guessed_mimetype
else:
mimetype = 'application/octet-stream'

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 _guess:
mimetype = guessed_mimetype or 'application/octet-stream'
if encoding is _guess:
encoding = guessed_encoding

response = _sendfile(request, filename, mimetype=mimetype)
if attachment:
if attachment_filename is None:
Expand All @@ -85,11 +95,11 @@ def sendfile(request, filename, attachment=False, attachment_filename=None, mime
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 not encoding:
encoding = guessed_encoding
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
8 changes: 5 additions & 3 deletions sendfile/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -90,7 +90,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="tests.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):
Expand All @@ -110,7 +112,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):
Expand Down