[Django]-Upload directly to S3 from django

4👍

Here’s a working example I once wrote, django-s3upload.

3👍

This isn’t too difficult. The steps are to generate a policy document, sign it, and then use that signature to POST the file to S3. I wrote a little application called sbit3 that does this. Take a look here: https://github.com/victortrac/sbit3/blob/master/server/sbit3.py, specifically the PostHandler class:

class PostHandler(tornado.web.RequestHandler):
    def _generate_policy_doc(self, conditions, expiration=None):
        if not expiration:
            # Sets a policy of 15 minutes to upload file
            expiration = datetime.datetime.utcnow() + datetime.timedelta(minutes=15)
        conditions = [ { "bucket" : conditions["bucket"] },
                       [ "starts-with", "$key", "uploads/"],
                       { "acl" : conditions["acl"] },
                       { "success_action_redirect" : conditions["success_action_redirect"] } ]
        conditions_json = json.dumps({ "expiration" : expiration.strftime("%Y-%m-%dT%H:%M:%SZ"),
                                       "conditions" : conditions })
        logging.debug("Policy doc generated: {0}".format(conditions_json))
        return base64.b64encode(conditions_json)

    def _sign_policy(self, policy):
        signature = base64.b64encode(hmac.new(settings.aws_secret_key, policy, hashlib.sha1).digest())
        return signature

    def get(self, expiration):
        try:
            expiration = int(expiration)
            # Set max expiration to 7200 minutes (5 days)
            if not 0 < expiration < 7200:
                raise tornado.web.HTTPError(403)
            _expireTimestamp = datetime.datetime.utcnow() + datetime.timedelta(minutes=expiration)
        except ValueError:
            raise tornado.web.HTTPError(403)

        # Associate _uuid to expiration in sdb

        _uuid = uuid.uuid4().hex
        sdb_conn.add_item(_uuid, expireTimestamp=_expireTimestamp)

        conditions = { "bucket" : settings.bucket,
                       "acl" : settings.acl,
                       "success_action_redirect" : settings.site_url + "/f/" + _uuid }
        policy_document = self._generate_policy_doc(conditions)
        signature = self._sign_policy(policy_document)

        self.render("post.html", conditions=conditions,
                                 aws_access_id=settings.aws_access_id,
                                 policy_document=policy_document,
                                 signature=signature)

The look at the post.html that sets up the form:

<form action="https://{{ conditions["bucket"] }}.s3.amazonaws.com" method="post" enctype="multipart/form-data">
  <input type="hidden" name="key" value="uploads/${filename}">
  <input type="hidden" name="AWSAccessKeyId" value="{{ aws_access_id }}"> 
  <input type="hidden" name="acl" value="{{ conditions["acl"] }}"> 
  <input type="hidden" name="success_action_redirect" value="{{ conditions["success_action_redirect"] }}">
  <input type="hidden" name="policy" value="{{ policy_document }}">
  <input type="hidden" name="signature" value="{{ signature }}">

  File to upload to S3: 
  <input name="file" type="file"> 
  <br> 
  <input type="submit" value="Upload File to S3"> 
</form> 

2👍

In order to upload directly to S3(bypassing your webserver) you will need to directly post through the browser to a pre-authorized url. Read this article from amazon that explains how it needs to work.

I don’t know of anything that will do this for you in django, but it is not too difficult to make the request yourself. You can also use something like uploadify to do the actual posting from the browser, you just need to give it the right url.

👤Kekoa

Leave a comment