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>
|
Comments