Last active
January 18, 2021 09:13
-
-
Save Skorch/90ab38295b959c979363a35fe6ba8665 to your computer and use it in GitHub Desktop.
Uploading from iOS to S3 as a background task. Pro Tip: You can embed REST parameters in the header, and have the server execute a Lambda on upload.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: | |
import Foundation | |
import AWSS3 | |
//A sample struct to define the parameters to encode and send to the | |
//server to be executed along with your upload | |
struct FileUploadParameters{ | |
//label for the encoded name | |
private static let EncodeParam1 = "param_1" | |
private static let EncodeParam2 = "param_2" | |
let param1: String | |
let param2: String | |
//Perform a simple serialization of the parameters | |
func encode() -> [String: AnyObject]{ | |
return [ | |
FileUploadParameters.EncodeParam1: param1, | |
FileUploadParameters.EncodeParam2: param2 | |
] | |
} | |
} | |
//Handles the creation of the AWS S3 upload with the encoding of the Lambda request embedded | |
private func uploadFile(uploadParameters: FileUploadParameters, localFileURL: NSURL, remoteBucketName: String, remoteFileName: String, fileMimeType: String) -> AWSTask?{ | |
do{ | |
//serialize the parameters to be included in the upload payload - will be executed on the server after upload is complete | |
let jsonParameters = try NSJSONSerialization.dataWithJSONObject(uploadParameters.encode(), options: NSJSONWritingOptions(rawValue: 0)) | |
//Need to encode string so it fits properly into the header | |
//Ensure UTF8 string enoding | |
//convert to URL percent encoding | |
guard | |
let jsonParametersToken = NSString(data: jsonParameters, encoding: NSUTF8StringEncoding), | |
let encodedParameters = jsonParametersToken.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLHostAllowedCharacterSet()) | |
else{ | |
LogWarn("Burst parameters could not be serialized") | |
return nil | |
} | |
//Store the encoded string into the custom metadata | |
//All custom metadata values begin with x-amz-meta- | |
//Could alternatively create one string-encoded parameter per value, instead of JSON serialization | |
let customMeta = [ | |
"x-amz-meta-uploadParameters": encodedParameters, | |
] | |
//AWS Transfer Utility manages the NSURLSession and background data task for you | |
let transferUtility = AWSS3TransferUtility.defaultS3TransferUtility() | |
//In order to customize the header information, we use the AWSS3TransferUtilityUploadExpression class | |
let expression = AWSS3TransferUtilityUploadExpression() | |
//We want our file to be publicly available by default | |
expression.setValue("public-read", forRequestParameter: "x-amz-acl") | |
//Copy the custom Meta information into the expression | |
for (key, metaValue) in customMeta{ | |
expression.setValue(metaValue, forRequestParameter: key) | |
} | |
//build and return the AWSTask | |
return transferUtility.uploadFile(localFileURL, bucket: remotBucketName, key: remoteFileName, contentType: fileMimeType, expression: expression, completionHander: { [remoteFileName, localFileURL] (task, error) -> Void in | |
if let e = error{ | |
print("upload of file \(remoteFileName) failed.\n\(e)") | |
return | |
} | |
//Clean up {localFileURL}? | |
}) | |
} catch let e{ | |
print("could not encode upload parameters \(e)") | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
The goal is to have your app upload a file to S3 without needing the app to be active. Typically one would perform a subsequent API call after the file has been successfully uploaded. If your app is in the background, this won't happen until the app is activated.
By using Amazon's
AWSS3TransferUtility
, their framework deals with the wholeNSURLSession
lifecycle and management.By using a technique where we're associating custom upload headers with a custom JSON payload, the exact information needed to process the subsequent API call is embedded in the upload itself.
On the server, we create a Lambda event on the S3 container. Now when the upload completes, a Lambda is executed, where we can now fetch the header information, parse the JSON payload, then perform the API call needed.
The end result is a background upload task that can complete while the app is in the background, and the API call is still executed once the upload is complete - fire and forget!