views:

396

answers:

2

I'm looking for a way to sell someone a card at an event that will have a unique code that they will be able to use later in order to download a file (mp3, pdf, etc.) only one time and mask the true file location so a savvy person downloading the file won't be able to download the file more than once. It would be nice to host the file on Amazon S3 to save on bandwidth where our server is co-located.

My thought for the codes would be to pre-generate the unique codes that will get printed on the cards and store those in a database that could also have a field that stores the number of times the file was downloaded. This way we could set how many attempts we would allow the user for downloading the file.

The part that I need direction on is how do I hide/mask the original file location so people can't steal that url and then download the file as many times as they want. I've done Google searches and I'm either not searching using the right keywords or there aren't very many libraries or snippets out there already for this type of thing.

I'm guessing that I might be able to rig something up using django.views.static.serve that acts as a sort of proxy between the actual file and the user downloading the file. The only drawback to this method I would think is that I would need to use the actual web server and wouldn't be able to store the file on Amazon S3.

Any suggestions or thoughts are greatly appreciated.

+2  A: 

You can just use something simple such as mod_xsendfile. This functionality is also available in other popular webservers such lighttpd or nginx.

It works like this: when enabled your application (e.g. a trivial PHP script) can send a special response header, causing the webserver to serve a static file.

If you want it to work with S3 you will need to handle each and every request this way, meaning the traffic will go through your site, from there to AWS, back to your site and back to the client. Does S3 support symbolic links / aliases? If so you might just redirect a valid user to one of the symbolic URLs and delete that symlink after a couple of hours.

yawn
+3  A: 

Neat idea. However, I would warn against the single-download method, because there is no guarantee that their first download attempt will be successful. Perhaps use a time-expiration method instead?

But it is certainly possible to do this with Django. Here is an outline of the basic approach:

  • Set up a django url for serving these files
  • Use a GET parameter which is a unique string to identify which file to get.
  • Keep a database table which has a FileField for the file to download. This table maps the unique strings to the location of the file on the file system.
  • To serve the file as a download, set the response headers in the view like this:

(path is the location of the file to serve)

with open(path, 'rb') as f:
    response = HttpResponse(f.read())
response['Content-Type'] = 'application/octet-stream';
response['Content-Disposition'] = 'attachment; filename="%s"' % 'insert_filename_here'
return response

Since we are using this Django page to serve the file, the user cannot find out the original file location.

Christian Oudard
This is the most basic approach, but could make your server hurt if you serve a lot of these files, as the entire file has to be streamed through a fat Python/Django server thread.
Carl Meyer
Yes, it's not a super fast solution, but it seems that in his situation performance is not going to be a problem. He has to physically sell each user a card, so I wouldn't expect anywhere close to even 100 downloads per day.
Christian Oudard