Introduction: download function
By default, download() function does some permission checks (if they are set) and sends the uploaded files to the client.
The problem: download prevents client side caching
The problem is that download() function sends the following http headers, that prevents client side caching:
- Expires: Thu, 27 May 2010 05:06:44 GMT
- Pragma: no-cache
- Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
This may be good for some dynamic content, but, for example, a client browsing a site with several non static images, will see how each image loads every time the page is shown, slowing down navigation.
Caching download() with @cache will not help, it prevents permission checks to be made, and cache is done at server side, we need some at client side.
One Solution: fast_download
A best approach will be using a custom download function, allowing client side caching, so once the browser downloaded a file, that file doesn't need to be downloaded again soon.
This can be done with a custom download function (ie. fast_download), doing simple security checks and modifying http headers to favor client side caching:
- Last-Modified: Tue, 04 May 2010 19:41:16 GMT
- Expires removed
- Pragma removed
- Cache-control removed
Using Last-Modified allows If-Modified-Since http requests, whose speed up downloads preventing sending again unmodified contents. response.stream handles if-modified-since and range requests automatically.
Other Solution: let webserver handle downloads
This aproach need some webserver (apache) config files tweaking, so is not portable nor easily configurable. Using fast_download should be a better alternative, that requires no custom configuration and works quickly with almost all webservers, and the user would not notice any performance difference.
So, in controller, default.py add:
def fast_download(): # very basic security (only allow fast_download on your_table.upload_field): if not request.args(0).startswith("yourtable.upload_field"): return download() # remove/add headers that prevent/favors client-side caching del response.headers['Cache-Control'] del response.headers['Pragma'] del response.headers['Expires'] filename = os.path.join(request.folder,'uploads',request.args(0)) # send last modified date/time so client browser can enable client-side caching response.headers['Last-Modified'] = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.localtime(os.path.getmtime(filename))) return response.stream(open(filename,'rb'))
In your view, remember to make URLs using fast_download instead of download function:
URL(r=request, c='default', f='fast_download', args=your_table.upload_field)
Thanks massimo for the advice and comments, for the full thread see: http://groups.google.com/group/web2py/browse_thread/thread/0c10235cb16c476f/0f2dd71668626b6d?show_docid=0f2dd71668626b6d&fwc=1