## On your app.forms.py

from member.models import UserProfile
from django import forms
import datetime

import base64
import hmac, sha, simplejson


class S3UploadForm(forms.Form):
    """
    http://developer.amazonwebservices.com/connect/entry.jspa?externalID=1434

    <input type="hidden" name="key" value="uploads/${filename}">
    <input type="hidden" name="AWSAccessKeyId" value="YOUR_AWS_ACCESS_KEY"> 
    <input type="hidden" name="acl" value="private"> 
    <input type="hidden" name="success_action_redirect" value="http://localhost/">
    <input type="hidden" name="policy" value="YOUR_POLICY_DOCUMENT_BASE64_ENCODED">
    <input type="hidden" name="signature" value="YOUR_CALCULATED_SIGNATURE">
    <input type="hidden" name="content_type" value="image/jpeg">
    """
    key = forms.CharField(widget = forms.HiddenInput)
    AWSAccessKeyId = forms.CharField(widget = forms.HiddenInput)
    acl = forms.CharField(widget = forms.HiddenInput)
    success_action_redirect = forms.CharField(widget = forms.HiddenInput)
    policy = forms.CharField(widget = forms.HiddenInput)
    signature = forms.CharField(widget = forms.HiddenInput)
    content_type = forms.CharField(widget = forms.HiddenInput)
    file = forms.FileField()
    
    def __init__(self, aws_access_key, aws_secret_key, bucket, key,
            expires_after = datetime.timedelta(days = 30),
            acl = 'public-read',
            success_action_redirect = None,
            content_type = '',
            min_size = 0,
            max_size = None,
        ):
        self.aws_access_key = aws_access_key
        self.aws_secret_key = aws_secret_key
        self.bucket = bucket
        self.key = key
        self.expires_after = expires_after
        self.acl = acl
        self.success_action_redirect = success_action_redirect
        self.content_type = content_type
        self.min_size = min_size
        self.max_size = max_size
        
        policy = base64.b64encode(self.calculate_policy())
        signature = self.sign_policy(policy)
        
        initial = {
            'key': self.key,
            'AWSAccessKeyId': self.aws_access_key,            
            'acl': self.acl,
            'policy': policy,
            'signature': signature,
            'content_type': self.content_type,
        }
        if self.success_action_redirect:
            initial['success_action_redirect'] = self.success_action_redirect
        
        super(S3UploadForm, self).__init__(initial = initial)
        
        if self.content_type:
            # we change the name of the field to the S3 required
            self.fields['content_type'].widget.attrs.update({'name':'Content-Type'})
        # We need to manually rename the content_type field to content_type
        if self.max_size:
            self.fields['MAX_SIZE'] = forms.CharField(widget=forms.HiddenInput)
            self.initial['MAX_SIZE'] = self.max_size
        
        # Don't show success_action_redirect if it's not being used
        if not self.success_action_redirect:
            del self.fields['success_action_redirect']

    def add_prefix(self, field_name):
        # Hack to use the S3 required field name
        if field_name == 'content_type' and self.content_type:
            field_name = 'Content-Type'
        return super(S3UploadForm, self).add_prefix(field_name)

    def as_html(self):
        """
        Use this instead of as_table etc, because S3 requires the file field
        come AFTER the hidden fields, but Django's normal form display methods
        position the visible fields BEFORE the hidden fields.
        """
        html = ''.join(map(unicode, self.hidden_fields()))
        html += unicode(self['file'])
        return html
    
    def as_form_html(self, prefix='', suffix=''):
        html = """
        <form action="%s" method="post" enctype="multipart/form-data">
        <p>%s <input type="submit" value="Upload"></p>
        </form>
        """.strip() % (self.action(), self.as_html())
        return html
    
    def is_multipart(self):
        return True
    
    def action(self):
        return 'https://%s.s3.amazonaws.com/' % self.bucket
    
    def calculate_policy(self):
        conditions = [
            {'bucket': self.bucket},
            {'acl': self.acl},
            ['starts-with', '$key', self.key.replace('${filename}', '')],
            ["starts-with", "$Content-Type", self.content_type],
        ]
        if self.content_type:
            conditions.append(
                ['starts-with','$Content-Type',self.content_type]
            )
        if self.max_size:
            conditions.append(
                ['content-length-range',self.min_size,self.max_size]
            )
        if self.success_action_redirect:
            conditions.append(
                {'success_action_redirect': self.success_action_redirect},
            )
        
        policy_document = {
            "expiration": (
                datetime.datetime.now() + self.expires_after
            ).isoformat().split('.')[0] + 'Z',
            "conditions": conditions,
        }
        return simplejson.dumps(policy_document, indent=2)
    
    def sign_policy(self, policy):
        return base64.b64encode(
            hmac.new(self.aws_secret_key, policy, sha).digest()
        )

## On a app.view.py

def profile_image(request,template_name=None,user=None):
    s3uploadform = S3UploadForm(
        settings.AWS_ACCESS_KEY_ID,
        settings.AWS_SECRET_ACCESS_KEY,
        settings.AWS_STORAGE_BUCKET_NAME,
        'uploads/'+user+'_photo.jpg',
        content_type='image/',
        success_action_redirect = 'http://domain/johnTheUser/upload_success',
        max_size=734003, # 700 kbs
        expires_after = datetime.timedelta(days=99999) #forever!
        )
    context = {'form':s3uploadform}
    return render_to_response(template_name,context,
                    context_instance=RequestContext(request))


## On a template.html

<html>
    <head>
        <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
        <script>
            $(function(){
                var max_size = {{form.max_size}};
                var allowed_file_extensions = ['png','jpg'];
                var $upload_form = $('#image_upload_form');
                $('#id_file').change(function(){
                    if($(this).val()){
                        file_name = $(this).val();
                        file_extension = file_name.substring(file_name.length - 3,file_name.length)
                        if($.inArray(file_extension,allowed_file_extensions) < 0){
                            alert('Wrong file type');
                            return false;
                        }
                        if($(this)[0].files[0].size > max_size){
                            alert('The file is too big.')
                            return false;
                        }
                        $upload_form.submit();
                    }
                    else{
                        return false;
                    }
                })
            })
        </script>
    </head>
    <form id='upload_form' action="https://{{BUCKET_NAME}}.amazonaws.com/" method="post" enctype="multipart/form-data">
    {{form.key}}
    {{form.AWSAccessKeyId}}
    {{form.acl}}
    {{form.success_action_redirect}}
    {{form.policy}}
    {{form.signature}}
    {{form.content_type}}
    {{form.file}}
    <input type="submit" value="Save" />
    </form>
</html>