Serving binary files "through" django

1,611 views
Skip to first unread message

Tim Sawyer

unread,
Apr 5, 2008, 3:19:00 PM4/5/08
to django...@googlegroups.com
Hi Folks,

I'd like to have a site that gives away and sells PDFs, and tracks downloads
of those PDFs. For example, I'd like to know the IP address/useragent of who
downloaded the free files, and I'd like to record the same plus the logged in
user for the pay ones (after authenticating the user is allowed to download
that file).

I've done this in Java by returning binary content (the PDF) from a servlet,
and having the servlet write to the database to say what was downloaded and
who downloaded it. I'd like to migrate this app to django (less
heavyweight!) can anyone point me towards a mechanism for doing this? I'm
fairly new at python but loving it...

Thanks,

Tim.

alex....@gmail.com

unread,
Apr 5, 2008, 3:40:50 PM4/5/08
to Django users
You would do something like this: http://www.djangoproject.com/documentation/outputting_pdf/
but instead of generating the pdf you could just serve a file object.

Matthias Kestenholz

unread,
Apr 5, 2008, 3:54:56 PM4/5/08
to django...@googlegroups.com

You could try something like that:

from django.http import HttpResponse
from testing.models import DownloadCounter

class MyFile(file):
def __init__(self, filename, *args, **kwargs):
self.filename = filename
super(MyFile, self).__init__(filename, *args, **kwargs)

def close(self):
super(MyFile, self).close()
cnt, created = DownloadCounter.objects.get_or_create(
filename=self.filename, defaults={'count':0})
cnt.count += 1
cnt.save()

def blah(request):
return HttpResponse(MyFile('views.py'),
mimetype='application/octet-stream')


(I am not running this code in production, and I don't know if I ever
will, but it was an interesting hack.)

This way, you can track file downloads when the download is completely
read from disk. If someone aborts a download, it wont be counted.

Thomas Kerpe

unread,
Apr 5, 2008, 4:42:57 PM4/5/08
to django...@googlegroups.com
Hello Tim

Am 05.04.2008 um 21:19 schrieb Tim Sawyer:
>
> I'd like to have a site that gives away and sells PDFs, and tracks
> downloads
> of those PDFs.

I hope I got your question right. I have a little bit different
aproach since django never serves the file directly.

If you are using nginx as a frontend-proxy and static file serving
just configure a protected-download-dir:

location /protected-downloads/ {
alias /var/protected/;
internal;
}

The "internal" is to disallow regular gets so your file is not
downloadable without the "OK" from your view.

Now you can send a X-Accel-Redirect Header from your view to let the
nginx send a file:

@login_required
def download(request, file=None):
response = HttpResponse()
response['Content-Type'] = 'application/octet-stream'
response['X-Accel-Redirect'] = '/protected-downloads/' + file
return response

Of course you can do additional processing in download as storing the
IP and download count to your database.
This soulution has the advantage that your files can be served very
quickly from your proxy instead of a relatively slow mod_python-apache.

If you are using Apache or lighttpd as a frontend-proxy you can use a
similar aproach with: X-Sendfile.
Or you use Amazon s3 etc...


Greets,
Thomas Kerpe

Eric Abrahamsen

unread,
Apr 6, 2008, 12:25:24 AM4/6/08
to django...@googlegroups.com
> If you are using Apache or lighttpd as a frontend-proxy you can use a
> similar aproach with: X-Sendfile.

I'd like to do something along these lines as well – keep files in a
protected directory and only allow them to be served if Django says
so. I'm serving static media from a different domain name (but the
same physical server) from my django application, which is running on
apache/mod_wsgi. From what I can tell X-Sendfile is a lighttpd-only
thing; is that true, and is there some other option using Apache?

Thanks
Eric

Eric Abrahamsen

unread,
Apr 6, 2008, 12:49:34 AM4/6/08
to Django users
Sorry, wrote too soon.

http://tn123.ath.cx/mod_xsendfile/

Guess I'll have to serve the files from django's apache, not my other
media server, but it seems like this will do it.

E

On Apr 6, 12:25 pm, Eric Abrahamsen <gir...@gmail.com> wrote:
> > If you are using Apache or lighttpd as a frontend-proxy you can use a
> > similar aproach with: X-Sendfile.
>
> I'd like to do something along these lines as well - keep files in a

Tim Sawyer

unread,
Apr 6, 2008, 4:41:00 PM4/6/08
to django...@googlegroups.com
On Saturday 05 Apr 2008, Matthias Kestenholz wrote:
> On Sat, 2008-04-05 at 20:19 +0100, Tim Sawyer wrote:
> > Hi Folks,
> >
> > I'd like to have a site that gives away and sells PDFs, and tracks
> > downloads of those PDFs. For example, I'd like to know the IP
> > address/useragent of who downloaded the free files, and I'd like to
> > record the same plus the logged in user for the pay ones (after
> > authenticating the user is allowed to download that file).
>
> You could try something like that:
>
> from django.http import HttpResponse
> from testing.models import DownloadCounter
>
> class MyFile(file):
> def __init__(self, filename, *args, **kwargs):
> self.filename = filename
> super(MyFile, self).__init__(filename, *args, **kwargs)
>
> def close(self):
> super(MyFile, self).close()
> cnt, created = DownloadCounter.objects.get_or_create(
> filename=self.filename, defaults={'count':0})
> cnt.count += 1
> cnt.save()
>
> def blah(request):
> return HttpResponse(MyFile('views.py'),
> mimetype='application/octet-stream')

Cheers everyone for your ideas, I'm going with the approach at the moment.

Thanks again,

Tim.

Thomas Kerpe

unread,
Apr 7, 2008, 6:52:08 AM4/7/08
to django...@googlegroups.com
Hi Eric

Am 06.04.2008 um 06:49 schrieb Eric Abrahamsen:
>
> Guess I'll have to serve the files from django's apache, not my other
> media server, but it seems like this will do it.

That depends on your setup.

You can also use a X-Sendfile or X-Accel-Redirect capable Server as
your frontend-Proxy. So your request goes this way:

1. Frontend-Proxy and static file server (i.e. Nginx od lighttpd)
if URL matches local file or rule: serve it
else: request file from Backend-Server (i.e. Apache)
2. Backend Server
handle urls as in urls.py or your resolving strategy and serve
dynamic Data accordingly.

In this way your Hostname is always the same. All Static files can be
served from your frontend-proxy with X-Accel-Redirect even private
data. You also can serve cached dynamic content directly from your
proxy without hitting the apache.

In larger setups the frontend-proxy also can handle the load-balancing
stuff.

//Thomas

Eric Abrahamsen

unread,
Apr 7, 2008, 8:55:56 AM4/7/08
to django...@googlegroups.com
> 1. Frontend-Proxy and static file server (i.e. Nginx od lighttpd)
> if URL matches local file or rule: serve it
> else: request file from Backend-Server (i.e. Apache)
> 2. Backend Server
> handle urls as in urls.py or your resolving strategy and serve
> dynamic Data accordingly.
>
> In this way your Hostname is always the same. All Static files can be
> served from your frontend-proxy with X-Accel-Redirect even private
> data. You also can serve cached dynamic content directly from your
> proxy without hitting the apache.
>
> In larger setups the frontend-proxy also can handle the load-balancing
> stuff.

Well that's about a foot and a half past my current server
configuration abilities, but I appreciate the direction, and I'll
start doing some reading! If you or anyone has any more resources you
can point me towards, I'd be grateful.

Thanks,
Eric

Reply all
Reply to author
Forward
0 new messages