diff --git a/sendfile/__init__.py b/sendfile/__init__.py index 67c0c1c..0437df6 100644 --- a/sendfile/__init__.py +++ b/sendfile/__init__.py @@ -32,9 +32,9 @@ def _get_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, attachment=False, attachment_filename=None, mimetype=None, encoding=None, no_file_check = False): + '''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 @@ -45,23 +45,38 @@ 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) + If no mimetype or encoding are specified, then they will be + guessed via the filename (using the standard python mimetypes + module) + + If no_file_Check is true, sendfile() will check that the file + exists on the filesystem where stated. If no_file_check is False, + then sendfile() will simply encode the path without checking for + its existence. In this case it also won't set the Content-Length + or Content-Encoding header, so your surrounding proxy will need to + do that as needed. This mode can be useful when using sendfile() + under a proxy atop a remote store (eg S3). + ''' _sendfile = _get_sendfile() - if not os.path.exists(filename): + if not no_file_check 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' - - response = _sendfile(request, filename, mimetype=mimetype) + + if not no_file_check: + guessed_mimetype, guessed_encoding = guess_type(filename) + if mimetype is None: + if guessed_mimetype: + mimetype = guessed_mimetype + else: + mimetype = 'application/octet-stream' + else: + mimetype = 'application/octet-stream' + + response = _sendfile(request, filename, mimetype=mimetype, + no_file_check=no_file_check) if attachment: if attachment_filename is None: attachment_filename = os.path.basename(filename) @@ -82,9 +97,10 @@ 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) + if not no_file_check: + response['Content-length'] = os.path.getsize(filename) response['Content-Type'] = mimetype - if not encoding: + if not encoding and not no_file_check: encoding = guessed_encoding if encoding: response['Content-Encoding'] = encoding diff --git a/sendfile/backends/_internalredirect.py b/sendfile/backends/_internalredirect.py index 0aa1b37..dc9d3c2 100644 --- a/sendfile/backends/_internalredirect.py +++ b/sendfile/backends/_internalredirect.py @@ -1,14 +1,22 @@ from django.conf import settings import os.path -def _convert_file_to_url(filename): +def _convert_file_to_url(filename, no_file_check = False): + """In the normal case (no_file_check is False) reduce the filepath + against the filesystem and content root. If no_file_check is + True, then don't do any of that and instead assume that the path + passed is correct and in its ultimate form. + + """ + if no_file_check: # We already a priori know that the path is + # correct and in its final form. + return filename relpath = os.path.relpath(filename, settings.SENDFILE_ROOT) - + url = [settings.SENDFILE_URL] while relpath: relpath, head = os.path.split(relpath) url.insert(1, head) - return u'/'.join(url) - + return u'/'.join(url) # Note: xlates from os.path.sep to '/' diff --git a/sendfile/backends/mod_wsgi.py b/sendfile/backends/mod_wsgi.py index 743fa9f..c3dba1d 100644 --- a/sendfile/backends/mod_wsgi.py +++ b/sendfile/backends/mod_wsgi.py @@ -4,11 +4,11 @@ def sendfile(request, filename, **kwargs): response = HttpResponse() - response['Location'] = _convert_file_to_url(filename) + response['Location'] = _convert_file_to_url( + filename, no_file_check = kwargs.get ("no_file_check", False)) # need to destroy get_host() to stop django # rewriting our location to include http, so that # mod_wsgi is able to do the internal redirect request.get_host = lambda: '' return response - diff --git a/sendfile/backends/nginx.py b/sendfile/backends/nginx.py index f541a88..e1b70ac 100644 --- a/sendfile/backends/nginx.py +++ b/sendfile/backends/nginx.py @@ -4,7 +4,8 @@ def sendfile(request, filename, **kwargs): response = HttpResponse() - url = _convert_file_to_url(filename) + url = _convert_file_to_url( + filename, no_file_check = kwargs.get ("no_file_check", False)) response['X-Accel-Redirect'] = url.encode('utf-8') return response diff --git a/sendfile/backends/simple.py b/sendfile/backends/simple.py index 2f103ea..f5529e2 100644 --- a/sendfile/backends/simple.py +++ b/sendfile/backends/simple.py @@ -11,16 +11,17 @@ def sendfile(request, filename, **kwargs): # Respect the If-Modified-Since header. statobj = os.stat(filename) - if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'), - statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): + if not was_modified_since( + request.META.get('HTTP_IF_MODIFIED_SINCE'), + statobj[stat.ST_MTIME], statobj[stat.ST_SIZE]): return HttpResponseNotModified() - - + + response = HttpResponse(File(file(filename, 'rb'))) response["Last-Modified"] = http_date(statobj[stat.ST_MTIME]) return response - + def was_modified_since(header=None, mtime=0, size=0): """ Was something modified since the user last downloaded it? @@ -52,4 +53,3 @@ def was_modified_since(header=None, mtime=0, size=0): except (AttributeError, ValueError, OverflowError): return True return False - diff --git a/sendfile/backends/xsendfile.py b/sendfile/backends/xsendfile.py index ccf3775..2acc2e9 100644 --- a/sendfile/backends/xsendfile.py +++ b/sendfile/backends/xsendfile.py @@ -3,6 +3,4 @@ def sendfile(request, filename, **kwargs): response = HttpResponse() response['X-Sendfile'] = unicode(filename).encode('utf-8') - return response -