Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added support for conditional responses to send_file

  • Loading branch information...
commit d0c6ad7d287e543fcc941aa2b42557e06b9dc142 1 parent 88d9315
@mitsuhiko authored
Showing with 36 additions and 5 deletions.
  1. +2 −0  CHANGES
  2. +1 −1  flask/app.py
  3. +33 −4 flask/helpers.py
View
2  CHANGES
@@ -17,6 +17,8 @@ Codename to be decided, release date to be announced.
``autoescape`` tag.
- refactored Flask internally. It now consists of more than a
single file.
+- :func:`flask.send_file` now emits etags and has the ability to
+ do conditional responses builtin.
Version 0.4
-----------
View
2  flask/app.py
@@ -328,7 +328,7 @@ def create_jinja_loader(self):
return PackageLoader(self.import_name)
def init_jinja_globals(self):
- """Callde directly after the environment was created to inject
+ """Called directly after the environment was created to inject
some defaults (like `url_for`, `get_flashed_messages` and the
`tojson` filter.
View
37 flask/helpers.py
@@ -12,6 +12,8 @@
import os
import sys
import mimetypes
+from time import time
+from zlib import adler32
# try to load the best simplejson implementation available. If JSON
# is not installed, we add a failing class.
@@ -24,7 +26,7 @@
except ImportError:
json_available = False
-from werkzeug import Headers, wrap_file
+from werkzeug import Headers, wrap_file, is_resource_modified
from flask.globals import session, _request_ctx_stack, current_app, request
from flask.wrappers import Response
@@ -199,7 +201,8 @@ def get_flashed_messages(with_categories=False):
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
- attachment_filename=None):
+ attachment_filename=None, add_etags=True,
+ cache_timeout=60 * 60 * 12, conditional=False):
"""Sends the contents of a file to the client. This will use the
most efficient method available and configured. By default it will
try to use the WSGI server's file_wrapper support. Alternatively
@@ -220,6 +223,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
.. versionadded:: 0.2
+ .. versionadded:: 0.5
+ The `add_etags`, `cache_timeout` and `conditional` parameters were added.
+ The default behaviour is now to attach etags.
+
:param filename_or_fp: the filename of the file to send. This is
relative to the :attr:`~Flask.root_path` if a
relative path is specified.
@@ -232,6 +239,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
a ``Content-Disposition: attachment`` header.
:param attachment_filename: the filename for the attachment if it
differs from the file's filename.
+ :param add_etags: set to `False` to disable attaching of etags.
+ :param conditional: set to `True` to enable conditional responses.
+ :param cache_timeout: the timeout in seconds for the headers.
"""
if isinstance(filename_or_fp, basestring):
filename = filename_or_fp
@@ -266,8 +276,27 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
file = open(filename, 'rb')
data = wrap_file(request.environ, file)
- return Response(data, mimetype=mimetype, headers=headers,
- direct_passthrough=True)
+ rv = Response(data, mimetype=mimetype, headers=headers,
+ direct_passthrough=True)
+
+ rv.cache_control.public = True
+ if cache_timeout:
+ rv.cache_control.max_age = cache_timeout
+ rv.expires = int(time() + cache_timeout)
+
+ if add_etags and filename is not None:
+ rv.set_etag('flask-%s-%s-%s' % (
+ os.path.getmtime(filename),
+ os.path.getsize(filename),
+ adler32(filename) & 0xffffffff
+ ))
+ if conditional:
+ rv = rv.make_conditional(request)
+ # make sure we don't send x-sendfile for servers that
+ # ignore the 304 status code for x-sendfile.
+ if rv.status_code == 304:
+ rv.headers.pop('x-sendfile', None)
+ return rv
def render_template(template_name, **context):
Please sign in to comment.
Something went wrong with that request. Please try again.