Skip to content

Instantly share code, notes, and snippets.

@fzembow
Created August 21, 2019 20:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fzembow/eda54ca61dc49e2dabc3f56c6643d555 to your computer and use it in GitHub Desktop.
Save fzembow/eda54ca61dc49e2dabc3f56c6643d555 to your computer and use it in GitHub Desktop.
# Signed URL generation demo
# Author: Fil Zembowicz <fil@formsort.com>
usage = "AWS_REGION=... AWS_BUCKET=... AWS_UPLOAD_FOLDER=... flask run"
import boto3
import os
import uuid
from flask import Flask, request, abort, jsonify
app = Flask(__name__)
AWS_REGION = os.getenv("AWS_REGION")
AWS_BUCKET = os.getenv("AWS_BUCKET")
AWS_UPLOAD_FOLDER = os.getenv("AWS_UPLOAD_FOLDER")
if not AWS_REGION or not AWS_BUCKET or not AWS_UPLOAD_FOLDER:
raise (AssertionError(usage))
if AWS_UPLOAD_FOLDER.startswith("/"):
raise AssertionError("AWS_UPLOAD_FOLDER should not have a leading slash")
MIN_SIZE_BYTES = 1
MAX_SIZE_BYTES = 10_000_000
@app.route("/presigned-post")
def presigned_post():
# You can pass other args from the formsort answer set.
# When creating the signedUrl, just include them like https://example.com/get-signed-url?something={{some_answer}}
# Filename will automatically be added onto that.
filename = request.args.get("filename")
if not filename:
abort(400)
filename, ext = os.path.splitext(filename)
# Mime types are also verified on the frontend per configuration set within Formsort,
# but you should also do it on the backend.
allowed_upload_mime_types = {".jpg": "image/jpg"}
if ext not in allowed_upload_mime_types:
abort(400)
mime_type = allowed_upload_mime_types[ext]
filename = f"{uuid.uuid4()}{ext}"
location = f"{AWS_UPLOAD_FOLDER}/{filename}"
headers = {
"acl": "public-read",
"Content-Type": mime_type,
"Cache-Control": "max-age=31536000,public",
}
conditions = [
{"acl": "public-read"},
{"Content-Type": mime_type},
{"Cache-Control": "max-age=31536000,public"},
# File sizes are also frontend-verified, but you should also verify on the backend.
["content-length-range", MIN_SIZE_BYTES, MAX_SIZE_BYTES],
{"key": location},
]
s3 = boto3.client("s3", region_name=AWS_REGION)
post = s3.generate_presigned_post(
Bucket=AWS_BUCKET, Key=location, Conditions=conditions, Fields=headers
)
# You must return these three keys since they are required to actually upload the content.
# If you specify an answerValue in the response, that will be saved in the Formsort answer set.
# Otherwise, the full url (url + key) will be saved. This way, if your bucket allows GET, you can view
# the image immediately after.
response = jsonify({"url": post["url"], "headers": headers, "key": location})
# This is just for the demo. In practice you will want to restrict to *.yourdomain.com and *.formsort.com
response.headers.add("Access-Control-Allow-Origin", "*")
return response
if __name__ == "__main__":
app.run(debug=True)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment