How to return static files passing through a view in django?

Viewed 26434

I need to return css files and js files according to specific logic. Clearly, static serve does not perform what I need. I have a view, whose render method uses logic to find the proper file, but then I have to return it. Technically, I can just read the file and stuff it into a HttpResponse object with the proper mime type, but I was wondering if there was a better strategy. (like fpassthru() in php)

9 Answers

What webserver software are you using?

At least for Apache and NginX, there is a module enabling you to use the X-SendFile HTTP header. The NginX website says Lighty can do this, too.

In your wrapper view:

...

abspath = '/most_secret_directory_on_the_whole_filesystem/protected_filename.css'

response = HttpResponse()
response['X-Sendfile'] = abspath

response['Content-Type'] = 'mimetype/submimetype'
# or let your webserver auto-inject such a header field
# after auto-recognition of mimetype based on filename extension

response['Content-Length'] = <filesize>
# can probably be left out if you don't want to hassle with getting it off disk.
# oh, and:
# if the file is stored via a models.FileField, you just need myfilefield.size

response['Content-Disposition'] = 'attachment; filename=%s.css' \
    % 'whatever_public_filename_you_need_it_to_be'

return response

Then you can connect the view via http://mysite.com/url_path/to/serve_hidden_css_file/.

You can use it anytime you need to do something upon a file being requested that should not be directly accessible to users, like limiting who can access it, or counting requests to it for stats, or whatever.

For Apache: http://tn123.ath.cx/mod_xsendfile/
For NginX: http://wiki.nginx.org/NginxXSendfile

Why not return an HttpResponseRedirect to the location of the correct static file?

Pass an iterator (such as the result of open()) to the HttpResponse constructor.

you can use below code in your view:
Note:in this function I return images but you can return every thing based your need and set your context_type

from django.http import HttpResponse,Http404
import os

def img_finder(request, img_name):
    try:
        with open(os.path.dirname(os.path.abspath(__file__)) + '/static/img/' + img_name, 'rb') as f:
            return HttpResponse(f.read(), content_type="image/jpeg")
    except IOError:
        raise Http404

Here the most simple and efficient way to do this.

app/urls.py

from django.urls import re_path
from app import views

urlpatterns = [
    re_path(r'^(?P<public_url>.*)$', views.public, name="public"),
]

Warning : put the URL pattern at the end

app/views.py

import os
from django.conf import settings
from django.views.static import serve

def public(request, public_url):
    public_folder = os.path.join(str(settings.BASE_DIR), 'folder_path')
    return serve(request, public_url, document_root=public_folder)

It should be wasteful to use django to serve static content (not to mention, several orders of magnitude slower).

I'd rather convert the view into a context processor and use the variables in templates to find what blocks to include.

Related