serving a static file via nginx requiring authentication from django

942 views
Skip to first unread message

Annie

unread,
Jun 28, 2009, 10:20:26 PM6/28/09
to Django users
I'm trying to this to work: users can download their files from their
account page from a url like this: http://example.com/account/download/a1234565789asedga-2/
for which django processes the authentication and then passes along
the info to nginx to serve the file.

However, I keep getting stuck. The view works in my local windows
machine (windows/apache), but not on my server (gentoo/nginx/fcgi).
*sigh* I'd really appreciate it if someone could take a look and help
me figure out what I'm doing wrong, please.

Here's the error message I'm getting from nginx:

2009/06/28 18:59:41 [error] 16316#0: *194 open() "/usr/
htmlBook.pdf.zip" failed (2: No such file or directory), client:
174.22.130.187, server: example.com, request: "GET /account/download/
a1234565789asedga-2/ HTTP/1.1", upstream: "fastcgi://127.0.0.1:1024",
host: "example.com", referrer: "http://example.com/account/shelf/"

I know what's wrong in the error message ('/usr/htmlBook.pdf.zip'),
but I can't tell *where* it's getting that incorrect location. As far
as I can tell, I'm sending it the actual location to grab the file and
file name ('/home/me/web/example.com/public/media/books/
Book.pdf.zip'), but apparently I'm not getting that from django to
nginx correctly.

Here are the relevant bits of code:

# part of the views.py

def download_file(request, dlkey=None, ftype=None):
if request.user.is_authenticated():
try:
k = Ebook.objects.select_related().filter
(ftype__exact=ftype).filter
(book__orderdetail__dlkey__exact=dlkey).filter
(book__orderdetail__medium__exact='E')[:1]
for e in k:
ebook = e.ebook
filename = os.path.join(PROTECTED_DIR, os.path.basename
(ebook))
response = HttpResponse()
response['X-Accel-Redirect'] = ebook
response['Content-Disposition'] =
"attachment;filename=" + ebook
return response
except Exception:
raise Http404


# part of the nginx configuration for the domain:

location ^~ /account/download/ {
include /etc/nginx/
fastcgi_params_django;
fastcgi_pass 127.0.0.1:1024;
alias /home/me/web/example.com/
public/media/books/;
}

location ^~ /media/books/ {
root /home/me/web/example.com/
public;
internal;
}

# in settings.py
PROTECTED_DIR = '/home/me/web/example.com/public/media/books/'

If I do this in the view:

response['X-Accel-Redirect'] = ebook

I get the error message from above with nginx trying to grab the file
from the wrong location, but the browser presents me with a correctly
named (broken) zip file for download. (WinRAR says "The archive is
either in unknown format or damaged.")

If I do what I actually think I should be doing in the view:

response['X-Accel-Redirect'] = filename

I get a django 404 page with this as the request URL after clicking on
the download link: http://example.com/home/me/web/example.com/public/media/books/Book.pdf.zip/

The frustrating thing is this works (I can save the requested zip file
and open it just fine) on my windows setup with this part changed in
the views.py:

for e in k:
ebook = e.ebook
filename = os.path.join(PROTECTED_DIR, os.path.basename
(ebook))
handle = open(filename, 'rb')
book = handle.read()
handle.close()
response = HttpResponse(book)
response['Content-Type'] = 'application/x-zip-
compressed'
response['Content-Disposition'] =
"attachment;filename=" + ebook
return response

When I add the open() bit in the live version and change to:

response['X-Accel-Redirect'] = book

it gives me a big old mess. (Presumably because I'm trying to give it
the file itself, rather than the URI that X-Accel-Redirect requires
[1].)

So, the conclusion I've come to after many hours of reading docs,
trial and error, googling, and researching errors is that I haven't a
clue where it is that I'm telling django to send the wrong URL to
nginx. Help!

Thanks -

Annie


[1] http://wiki.nginx.org/NginxXSendfile

John Hensley

unread,
Jun 30, 2009, 1:00:16 AM6/30/09
to django...@googlegroups.com
On 6/28/09 10:20 PM, Annie wrote:
> I'm trying to this to work: users can download their files from their
> account page from a url like this: http://example.com/account/download/a1234565789asedga-2/
> for which django processes the authentication and then passes along
> the info to nginx to serve the file.

[...]

[...]

> [1] http://wiki.nginx.org/NginxXSendfile

I think the answer's in that document, actually. The value of
X-Accel-Redirect should be your internal location's URL
('/media/books/') plus your filename (os.path.basename(ebook), in your
view). Nginx will deliver
/home/me/web/example.com/public/media/books/ebook-basename.pdf.

John

Skylar Saveland

unread,
Jul 1, 2009, 4:34:55 AM7/1/09
to Django users
You could document this experience once you get it running; sounds
useful.

Alvin

unread,
Jul 1, 2009, 5:22:57 AM7/1/09
to Django users
I'll be writing a blog post about this soon

when John wrote is right on though it took me a while to wrap my mind
around it

the nginx location + nginx alias + url passed = file to be served

so to get file /home/me/web/example.com/download/file.pdf

location ^~ /download {
internal;
root /home/me/web/example.com/
}

would be accessed via:

response['X-Accel-Redirect'] = '/download/file.pdf'
return response

Annie

unread,
Jul 1, 2009, 9:59:42 AM7/1/09
to Django users
See, I knew it was probably something simple that I'd missed because
I'd been looking at this for too long. *g* I changed the PROTECTED_DIR
to '/media/books/' to match what I had in my nginx.conf and changed
the response['X-Accel-Redirect'] to filename in views.py and it works
perfectly now. (I thought I'd tried that variation of the location,
but it must've been earlier before I actually had the rest of it
working. *g*) Thanks, John!

I'll write this all up in full shortly on my blog
(herself.movielady.net), as I had a hard time finding detailed info
that combined this particular combination of software. :)

Thanks for the help, folks -
Annie
Reply all
Reply to author
Forward
0 new messages