34

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)

Stefano Borini
  • 138,652
  • 96
  • 297
  • 431
  • 1
    Which one is not performing what you need, webserver static file serving, or `django.views.static.serve` ? – Clément Feb 19 '10 at 13:07
  • actually, grammar question. is it "neither of" or "both" ? – Stefano Borini Feb 19 '10 at 15:24
  • Mmh, this is where I'm a bit confused. You see, in your question you say that you can basically _read the file and stuff it into an HttpResponse_. So I'm assuming these files exist __as is__ (ie: not dynamically generated) somewhere on your filesystem. What prevent you from serving them directly with the webserver? Or am I missing something? – Clément Feb 19 '10 at 16:54
  • I'd guess _neither of the two_, _neither of them_ :) – Clément Feb 19 '10 at 16:55

9 Answers9

34

This is what I used:

test_file = open('/home/poop/serve/test.pdf', 'rb')
response = HttpResponse(content=test_file)
response['Content-Type'] = 'application/pdf'
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' \
                                  % 'whatever'
return response
vabada
  • 1,738
  • 4
  • 29
  • 37
Uku Loskit
  • 40,868
  • 9
  • 92
  • 93
  • I needed the 'rb' mode on the open() function, or this code messed up my pdfs. This is matches the code used in django/views/static.py. – Aidan Ewen May 14 '12 at 14:20
  • 3
    It seems to me that this causes the whole file to be loaded in memory . Isn't it? – Emanuele Paolini Mar 12 '13 at 06:08
  • @EmanuelePaolini: you can pass an iterator to `HttpResponse` instead, I've modified the answer to reflect this. – Flimm Feb 02 '16 at 17:41
  • Thank you! I substituted `urllib2.urlopen(url_path).read()` for `open(path)` to allow for a pass-through view, serving pdf from external site: [1] `url = "https://s3.amazonaws.com/bucketname/path-to-external-file.pdf"` [2] `import urllib2` [3] `test_file = urllib2.urlopen(url).read()` [4] `response = HttpResponse(content=test_file)` [5] `response['Content-Type'] = 'application/pdf'` [6] `response['Content-Disposition'] = 'attachment; filename="%s.pdf"' % 'whatever'` [7] `return response` – Mark Jan 21 '19 at 17:10
  • @EmanuelePaolini better answer below by Ignacio Vazquez-Abrams, using iterator to return files in chunks rather than loading full file in memory. – Ammad Khalid Apr 04 '20 at 08:54
11

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

Geradeausanwalt
  • 2,040
  • 1
  • 17
  • 16
6

Why don't you use Django staticfiles inside your view

from django.contrib.staticfiles.views import serve

...
def view_function(request):
   return serve(request, 'absolute_path_to_file_name')
Gunnar Scherf
  • 93
  • 1
  • 1
  • 15
    The [docs](https://docs.djangoproject.com/en/1.4/ref/contrib/staticfiles/#other-helpers) say: "Warning. This view will only work if DEBUG is True. That’s because this view is grossly inefficient and probably insecure. This is only intended for local development, and should never be used in production." – Philipp Zedler Feb 12 '13 at 12:47
  • I just wanted it. thanks a lot. @PhilippZedler no problem, we can config these file in production. But to serve static files like /manifest.json and /servicewoker.js in development, I think it is the best way. – Al Mahdi Jul 08 '21 at 09:00
  • But it show SuspiciousFileOperation error – Al Mahdi Jul 08 '21 at 09:17
  • It only allows to serve files from the static folders in your project. Use (from django.views.static import serve) , and also add document_root argument to the function. – Al Mahdi Jul 09 '21 at 16:47
2

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

Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
2

Serving files directly from a view is very slow. If you are looking for normal file serving see this question: Having Django serve downloadable files

To very easily serve files through a view (for debug purposes, for example) keep reading.

# In your urls.py:
url(r'^test-files/(?P<name>.+)/$', views.test_files, name='test_files'),

# In your views.py:
from django.http.response import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt # (Allows file download with POST requests, can be omitted)
def test_files(request, name):
    if name == "myxml":
        fsock = open("/djangopath/data/static/files/my.xml", "rb")
        return HttpResponse(fsock)

This allows you to download the file from: http://127.0.0.1:8080/app/test-files/myxml/

Community
  • 1
  • 1
Lindlof
  • 2,152
  • 2
  • 17
  • 26
1

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

Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
1

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
PouriaDiesel
  • 660
  • 8
  • 11
0

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)
Al Mahdi
  • 635
  • 1
  • 9
  • 16
-2

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.

lprsd
  • 84,407
  • 47
  • 135
  • 168
  • that's exactly what I want to move away from. I am already including the stuff, with the result that the css and js files are included hardcoded into the html file, preventing any form of caching (since the page as a whole is deemed to change). I want to be able to ship js and css, but since this shipment is dependent on the plugins of my application that are available, I need to perform some logic before shipping the file. – Stefano Borini Feb 19 '10 at 07:14
  • You mean preventing any form of Django caching? Because having the css & js includes hard-coded into the template won't stop the user's browser from caching them. I'm not clear on what the actual problem is. It sounds like you're having trouble caching the entire page due to the dynamic includes. Why not cache the rest of the page and let the browser/ your web server do the rest (which is its job anyway)? – Tom Feb 20 '10 at 14:52
  • This would make a great comment or a caveat after a real answer. – Greg Schmit Jan 02 '17 at 22:52