Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
[Heroku] Direct to S3 File Uploads in Python
Fix for some issues with the original code from Heroku:
This example is also designed for use with Django, not Flask as in the original.
import base64
import hashlib
import hmac
import logging
import os
import time
import urllib
from hashlib import sha1
from django.http import HttpResponseRedirect
from django.http import JsonResponse
from django.shortcuts import render
from .forms import UploadForm
logger = logging.getLogger(__name__)
def upload(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = UploadForm(request.POST, request.FILES)
# check whether it's valid:
if form.is_valid():
return HttpResponseRedirect('/uploads/done/')
# if a GET (or any other method) we'll create a blank form
form = UploadForm()
return render(request, 'uploads/upload_form.html', {'form': form})
def sign_s3(request):
AWS_ACCESS_KEY = os.environ.get('AWS_ACCESS_KEY')
AWS_SECRET_KEY = os.environ.get('AWS_SECRET_KEY')
S3_BUCKET = os.environ.get('S3_BUCKET')
object_name = urllib.parse.quote_plus(request.GET['file_name'])
mime_type = request.GET['file_type']
secondsPerDay = 24*60*60
expires = int(time.time()+secondsPerDay)
amz_headers = "x-amz-acl:public-read"
string_to_sign = "PUT\n\n%s\n%d\n%s\n/%s/%s" % (mime_type, expires, amz_headers, S3_BUCKET, object_name)
encodedSecretKey = AWS_SECRET_KEY.encode()
encodedString = string_to_sign.encode()
h =, encodedString, sha1)
hDigest = h.digest()
signature = base64.encodebytes(hDigest).strip()
signature = urllib.parse.quote_plus(signature)
url = '' % (S3_BUCKET, object_name)
return JsonResponse({
'signed_request': '%s?AWSAccessKeyId=%s&Expires=%s&Signature=%s' % (url, AWS_ACCESS_KEY, expires, signature),
'url': url,
def submit_form():
username = request.form["username"]
full_name = request.form["full_name"]
avatar_url = request.form["avatar_url"]
update_account(username, full_name, avatar_url)
return redirect(url_for('profile'))

This comment has been minimized.

Copy link

@arseni-mourzenko arseni-mourzenko commented Nov 22, 2015

Note that you're expected to include import urllib.parse, otherwise you'll probably get a:

AttributeError: 'module' object has no attribute 'parse'

around the line 49 urllib.parse.quote_plus.


This comment has been minimized.

Copy link

@robintan robintan commented Jun 25, 2016

Instead of import import urllib.parse
Line 49 should be changed to object_name = urllib.quote_plus(request.GET['file_name']) instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.