Hi everyone,
Today I’ve been converting my “PUT” upload to S3 to a “POST”. The main motivator for this was to restrict the file size of uploads using a signed policy. Unfortunately this was a pretty tedious process and the error responses from S3 were very vague. Thankfully it’s working now and here’s what I needed to do!
Here’s an excerpt from my nodejs lambda function that generates the presigned url:
/* Creates a pre-signed upload url and then stores a reference in a table */ exports.createUploadUrl = async params => { var { databaseHandler, bucketHandler } = params; // Create id for upload var uploadId = uuidv4(); // Retrieve pre-signed url var bucketDataPromise = createPresignedPostPromise({ Bucket: process.env.BUCKET_UPLOADS, Expires: 60 * 60, Conditions: [ ["content-length-range", 0, 300000], // 300kb [ "eq", "$key", uploadId], [ "eq", "$Content-Type", 'image/jpeg' ], ] }); // var ddbData = await ddbDataPromise; var bucketData = await bucketDataPromise; // Wait for database handler to complete operation and then return return Helpers.generateResponse({ data: { uploadData: bucketData, additionalFields: { key: uploadId, "Content-Type": 'image/jpeg', }, }, statusCode: 200 }); }
You can then retrieve the upload details using a request like the following (Python):
resp = requests.put("https://f86caxqe9f.execute-api.ap-southeast-2.amazonaws.com/Prod/images", data=open(path, 'rb'))
This will return a response similar to the following:
{ "messages": [], "data": { "uploadUrl": { "url": "YOUR UPLOAD URL", "fields": { "bucket": "YOUR BUCKET", "X-Amz-Algorithm": "AWS4-HMAC-SHA256", "X-Amz-Credential": "XXX", "X-Amz-Date": "20190107T125044Z", "X-Amz-Security-Token": "SECURITY_TOKEN", "Policy": "YOUR BASE64 ENCODED POLICY", "X-Amz-Signature": "SIGNATURE" } }, "uploadId": "UPLOAD_ID" } }
And once you have those details you can use them to upload the image:
# Attempt to retrieve upload url etc json = resp.json() data = json["data"] uploadUrl = data["uploadData"]["url"] uploadFields = data["uploadData"]["fields"] uploadFields.update(data["additionalFields"]) try: print("Uploading image...",end='') headers = {'Content-Type': 'multipart/form-data' } files = { 'file': open(path, 'rb')} resp = requests.post(uploadUrl, data=uploadFields, files=files) # Only show content if there's an error if resp.status_code == 200: print("Uploaded") else: print("ERROR") print(repr(resp)) print(repr(resp.content)) except Exception as e: print("\r\nFailed to upload image.") print("Upload data:") print(repr(uploadFields)) print("Exception:") print(repr(e)) traceback.print_exc()
Hopefully that’s been able to help you out, but feel free to let me know in the comments below if you need more info!
Thanks to these links for the info:
https://stackoverflow.com/a/35923104/522859
https://stackoverflow.com/a/49311831/522859
Leave a Reply