S3 Uploader
Let's say you want to upload a file to one of your S3 buckets, using POST.
Your API has a protected endpoint that returns the necessary S3 upload params. Maybe it uses Boto to generate a presigned upload URL.
A successful request to this endpoint returns something like this:
{
"fields": {
"AWSAccessKeyId": "AKIAJSQUO7ORWYVCSV6Q",
"acl": "public-read",
"key": "files/89789486-d94a-4251-a42d-18af752ab7d2-test.txt",
"policy": "eyJleHBpcmF0aW9uIjogIjIwMTgtMTAtMzBUMjM6MTk6NDdaIiwgImNvbmRpdGlvbnMiOiBbeyJhY2wiOiAicHVibGljLXJlYWQifSwgWyJjb250ZW50LWxlbmd0aC1yYW5nZSIsIDEwLCAzMTQ1NzI4MF0sIHsiYnVja2V0IjogImJlYW10ZWNoLWZpbGUifSwgeyJrZXkiOiAiY29tcGFueS8zLzg5Nzg5NDg2LWQ5NGEtNDI1MS1hNDJkLTE4YWY3NTJhYjdkMi10ZXN0LnR4dCJ9XX0=",
"signature": "L7r3KBtyOXjUKy31g42JTYb1sio="
},
"fileUrl": "https://my-bucket.s3.amazonaws.com/files/89789486-d94a-4251-a42d-18af752ab7d2-test.txt",
"uploadUrl": "https://my-bucket.s3.amazonaws.com/"
}
fields has everything you need to authenticate with your S3 bucket, but you need to add them to the request sent by RDU. It turns out this is super easy.
const getUploadParams = async ({ meta: { name } }) => {
const { fields, uploadUrl, fileUrl } = await myApiService.getPresignedUploadParams(name)
return { fields, meta: { fileUrl }, url: uploadUrl }
}
That's it. If myApiService.getPresignedUploadParams succeeds, you return uploadUrl as url. You also decide to merge fileUrl into your file's meta so you can use it later. RDU takes care of the rest, including appending the fields to the FormData instance used in the XMLHttpRequest.
Let's say myApiService.getPresignedUploadParams fails and returns {}. In this case uploadUrl and hence url are undefined. RDU abandons the upload and changes the file's status to 'error_upload_params'. At this point you might show the user an error message, and the user might remove the file or restart the upload.
S3 using PUT instead of POST
Uploading a file to S3 using PUT works differently than using POST.
Basically, if you use PUT, you can't wrap your file in a FormData instance. In this case, body must be set to file, and the fields in the POST example are all encoded in the query string of the uploadUrl. getUploadParams would look a little different:
const getUploadParams = async ({ file, meta: { name } }) => {
const { uploadUrl, fileUrl } = await myApiService.getPresignedUploadParams(name)
return { body: file, meta: { fileUrl }, url: uploadUrl }
}