This is a 'fixed' version of snippet 1868 Changes: Correctly handle the Content-Type, because amazon requieres it to be named with a dash and we can't use dashes in the form attributes declaration. Also added max_size handling, with the corresponding update to the policy generation. *Added an example usage with some javascript for basic validation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | ## 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>
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 2 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
Please login first before commenting.