Using TransferUtility

You are currently viewing the AWS SDK for Mobile documentation which is a collection of low-level libraries. Use the Amplify libraries for all new app development. Learn more

You can view the Mobile SDK API reference here.

To make it easy to upload and download objects from Amazon S3, we provide a TransferUtility component with built-in support for background transfers, progress tracking, and MultiPart uploads. This section explains how to implement upload and download functionality and a number of additional storage use cases.

Note: If you use the transfer utility MultiPart upload feature, take advantage of automatic cleanup features by setting up the AbortIncompleteMultipartUpload action in your Amazon S3 bucket life cycle configuration.

Transfer Utility Options

You can use the AWSS3TransferUtilityConfiguration object to configure the operations of the TransferUtility.

isAccelerateModeEnabled

The isAccelerateModeEnabled option allows you to upload and download content from a S3 bucket that has Transfer Acceleration enabled. This option is set to false by default. See Transfer Acceleration for information on how to enable transfer acceleration for your bucket.

The code sample below manually sets up credentials for the TransferUtility. The best practice is to use the AWSMobileClient. See Authentication for more details

1//Setup credentials, see your awsconfiguration.json for the "YOUR-IDENTITY-POOL-ID"
2let credentialProvider = AWSCognitoCredentialsProvider(regionType: YOUR-IDENTITY-POOL-REGION, identityPoolId: "YOUR-IDENTITY-POOL-ID")
3
4//Setup the service configuration
5let configuration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: credentialProvider)
6
7//Setup the transfer utility configuration
8let tuConf = AWSS3TransferUtilityConfiguration()
9tuConf.isAccelerateModeEnabled = true
10
11//Register a transfer utility object asynchronously
12AWSS3TransferUtility.register(
13 with: configuration!,
14 transferUtilityConfiguration: tuConf,
15 forKey: "transfer-utility-with-advanced-options"
16) { (error) in
17 if let error = error {
18 //Handle registration error.
19 }
20}
21
22//Look up the transfer utility object from the registry to use for your transfers.
23let transferUtility:(AWSS3TransferUtility?) = AWSS3TransferUtility.s3TransferUtility(forKey: "transfer-utility-with-advanced-options")

retryLimit

The retryLimit option allows you to specify the number of times the TransferUtility will retry a transfer when it encounters an error. By default it is set to 0.

1tuConf.retryLimit = 5

multiPartConcurrencyLimit

The multiPartConcurrencyLimit option allows you to specify the number of parts that will be uploaded in parallel for a MultiPart upload request. By default, this is set to 5.

1tuConf.multiPartConcurrencyLimit = 3

timeoutIntervalForResource

The timeoutIntervalForResource parameter allows you to specify the maximum duration the transfer will run. The default value for this parameter is 50 minutes. This value is important if you use Amazon Cognito temporary credential because it aligns with the maximum span of time that those credentials are valid.

1tuConf.timeoutIntervalForResource = 15*60 //15 minutes

Upload a File

The transfer utility provides methods for both single-part and multipart uploads. When a transfer uses multipart upload, the data is chunked into a number of 5 MB parts which are transferred in parallel for increased speed.

The following example shows how to upload a file to an S3 bucket.

1func uploadData() {
2
3 let data: Data = Data() // Data to be uploaded
4
5 let expression = AWSS3TransferUtilityUploadExpression()
6 expression.progressBlock = {(task, progress) in
7 DispatchQueue.main.async(execute: {
8 // Do something e.g. Update a progress bar.
9 })
10 }
11
12 var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
13 completionHandler = { (task, error) -> Void in
14 DispatchQueue.main.async(execute: {
15 // Do something e.g. Alert a user for transfer completion.
16 // On failed uploads, `error` contains the error object.
17 })
18 }
19
20 let transferUtility = AWSS3TransferUtility.default()
21
22 transferUtility.uploadData(data,
23 bucket: "YourBucket",
24 key: "YourFileName",
25 contentType: "text/plain",
26 expression: expression,
27 completionHandler: completionHandler).continueWith {
28 (task) -> AnyObject? in
29 if let error = task.error {
30 print("Error: \(error.localizedDescription)")
31 }
32
33 if let _ = task.result {
34 // Do something with uploadTask.
35 }
36 return nil;
37 }
38}

The following example shows how to upload a file to an S3 bucket using multipart uploads.

1func uploadData() {
2
3 let data: Data = Data() // Data to be uploaded
4
5 let expression = AWSS3TransferUtilityMultiPartUploadExpression()
6 expression.progressBlock = {(task, progress) in
7 DispatchQueue.main.async(execute: {
8 // Do something e.g. Update a progress bar.
9 })
10 }
11
12 var completionHandler: AWSS3TransferUtilityMultiPartUploadCompletionHandlerBlock
13 completionHandler = { (task, error) -> Void in
14 DispatchQueue.main.async(execute: {
15 // Do something e.g. Alert a user for transfer completion.
16 // On failed uploads, `error` contains the error object.
17 })
18 }
19
20 let transferUtility = AWSS3TransferUtility.default()
21
22 transferUtility.uploadUsingMultiPart(data:data,
23 bucket: "YourBucket",
24 key: "YourFileName",
25 contentType: "text/plain",
26 expression: expression,
27 completionHandler: completionHandler).continueWith {
28 (task) -> AnyObject! in
29 if let error = task.error {
30 print("Error: \(error.localizedDescription)")
31 }
32
33 if let _ = task.result {
34 // Do something with uploadTask.
35 }
36 return nil;
37 }
38}

Download a File

The following example shows how to download a file from an S3 bucket.

1func downloadData() {
2 let expression = AWSS3TransferUtilityDownloadExpression()
3 expression.progressBlock = {(task, progress) in DispatchQueue.main.async(execute: {
4 // Do something e.g. Update a progress bar.
5 })
6 }
7
8 var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?
9 completionHandler = { (task, URL, data, error) -> Void in
10 DispatchQueue.main.async(execute: {
11 // Do something e.g. Alert a user for transfer completion.
12 // On failed downloads, `error` contains the error object.
13 })
14 }
15
16 let transferUtility = AWSS3TransferUtility.default()
17 transferUtility.downloadData(
18 fromBucket: "YourBucket",
19 key: "YourFileName",
20 expression: expression,
21 completionHandler: completionHandler
22 ).continueWith {
23 (task) -> AnyObject! in if let error = task.error {
24 print("Error: \(error.localizedDescription)")
25 }
26
27 if let _ = task.result {
28 // Do something with downloadTask.
29
30 }
31 return nil;
32 }
33}

Track Transfer Progress

Implement progress and completion actions for transfers by passing a progressBlock and completionHandler blocks to the call to AWSS3TransferUtility that initiates the transfer.

The following example of initiating a data upload, shows how progress and completion handling is typically done for all transfers. The AWSS3TransferUtilityUploadExpression, AWSS3TransferUtilityMultiPartUploadExpression and AWSS3TransferUtilityDownloadExpression contains the progressBlock that gives you the progress of the transfer which you can use to update the progress bar.

1// For example, create a progress bar
2let progressView: UIProgressView! = UIProgressView()
3progressView.progress = 0.0;
4
5let data = Data() // The data to upload
6
7let expression = AWSS3TransferUtilityUploadExpression()
8expression.progressBlock = {(task, progress) in DispatchQueue.main.async(execute: {
9 // Update a progress bar.
10 progressView.progress = Float(progress.fractionCompleted)
11 })
12}
13
14let completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock = { (task, error) -> Void in DispatchQueue.main.async(execute: {
15 if let error = error {
16 NSLog("Failed with error: \(error)")
17 }
18 else if(self.progressView.progress != 1.0) {
19 NSLog("Error: Failed.")
20 } else {
21 NSLog("Success.")
22 }
23 })
24}
25
26var refUploadTask: AWSS3TransferUtilityTask?
27let transferUtility = AWSS3TransferUtility.default()
28transferUtility.uploadData(data,
29 bucket: "S3BucketName",
30 key: "S3UploadKeyName",
31 contentType: "text/plain",
32 expression: expression,
33 completionHandler: completionHandler).continueWith { (task) -> AnyObject! in
34 if let error = task.error {
35 print("Error: \(error.localizedDescription)")
36 }
37
38 if let uploadTask = task.result {
39 // Do something with uploadTask.
40 // The uploadTask can be used to pause/resume/cancel the operation, retrieve task specific information
41 refUploadTask = uploadTask
42 }
43
44 return nil;
45 }

Pause a Transfer

To pause a transfer, retain references to AWSS3TransferUtilityUploadTask, AWSS3TransferUtilityMultiPartUploadTask or AWSS3TransferUtilityDownloadTask .

As described in the previous section :ref:native-track-progress-and-completion-of-a-transfer, the variable refUploadTask is a reference to the UploadTask object that is retrieved from the continueWith block of an upload operation that is invoked through transferUtility.uploadData.

To pause a transfer, use the suspend method:

1refUploadTask.suspend()

Note that pause internally uses the suspend api of NSURLSessionTask, which suspends the task temporarily and does not fully pause the transfer. Complete pausing of task is tracked in this Github issue - https://github.com/aws-amplify/aws-sdk-ios/issues/3668

Resume a Transfer

To resume a transfer, use the resume method:

1refUploadTask.resume()

Cancel a Transfer

To cancel a transfer, use the cancel method:

1refUploadTask.cancel()

Background Transfers

All transfers performed by TransferUtility for iOS happen in the background using NSURLSession background sessions. Once a transfer is initiated, it will continue regardless of whether the initiating app moves to the foreground, moves to the background, is suspended, or is terminated by the system. Note that this doesn't apply when the app is force-closed. Transfers initiated by apps that are force-closed are terminated by the operating system at the NSURLSession level. For regular uploads and downloads you will have to re-initiate the transfer. For multi-part uploads, the TransferUtility will resume automatically and will continue the transfer.

To wake an app that is suspended or in the background when a transfer is completed, implement the handleEventsForBackgroundURLSession method in the AppDelegate and have it call the interceptApplication method of AWSS3TransferUtility as follows.

1func application(
2 _ application: UIApplication,
3 handleEventsForBackgroundURLSession identifier: String,
4 completionHandler: @escaping () -> Void
5) {
6 // Store the completion handler.
7 AWSS3TransferUtility.interceptApplication(
8 application,
9 handleEventsForBackgroundURLSession: identifier,
10 completionHandler: completionHandler
11 )
12}

Managing Transfers When an App Restarts

When an app that has initiated a transfer restarts (if it has been terminated by the system and not force-closed), it is possible that the transfer may still be in progress or completed. Tasks for uploads, downloads and multipart uploads can be monitored with progress and completion handlers. In the code below accessing the default instance of the Transfer Utility has a completion handler which is run once the recovery process has completed. Any network operations which were still running and were persisted by Transfer Utility are restored. Once the completion handler is done it is ready to attach handlers for progress and completion updates.

1init() {
2 transferUtility = AWSS3TransferUtility.default(completionHandler: { error in
3 if let error = error {
4 print("Error: \(error)")
5 return
6 }
7 self.reattachHandlers()
8 })
9}
10func reattachHandlers() {
11 let blocks = AWSS3TransferUtilityBlocks(
12 uploadProgress: nil,
13 multiPartUploadProgress: handleProgress(task:progress:),
14 downloadProgress: nil,
15 uploadCompleted: nil,
16 multiPartUploadCompleted: handleCompletion(task:error:),
17 downloadCompleted: nil
18 )
19 transferUtility.enumerateToAssign(blocks: blocks)
20}

This code is only attaching handlers for multipart uploads. The other handlers are left as nil but they could be defined as well depending on your needs. Handlers are given the task which includes the transferID property which can be used to associate with network operations. Place this initialization routine at the start of your app launch lifecycle.

Manage a Transfer when a Suspended App Returns to the Foreground

When an app that has initiated a transfer becomes suspended and then returns to the foreground, the transfer may still be in progress or may have completed. In both cases, use the following code to re-establish the progress and completion handler blocks of the app.

1if let uploadTasks = transferUtility.getUploadTasks().result {
2 for task in uploadTasks {
3 task.setCompletionHandler(completionHandler)
4 task.setProgressBlock(progressBlock)
5 }
6}
7
8if let downloadTasks = transferUtility.getDownloadTasks().result {
9 for task in downloadTasks {
10 task.setCompletionHandler(completionHandler)
11 task.setProgressBlock(progressBlock)
12 }
13}
14
15if let multiPartUploadTasks = transferUtility.getMultiPartUploadTasks().result {
16 for task in multiPartUploadTasks {
17 task.setCompletionHandler(completionHandler)
18 task.setProgressBlock(progressBlock)
19 }
20}

Transfer with Object Metadata

The AWSS3TransferUtilityUploadExpression and AWSS3TransferUtilityMultiPartUploadExpression classes contain the method setValue:forRequestHeader where you can pass in metadata to Amazon S3. This example demonstrates passing in the Server-side Encryption Algorithm as a request header in uploading data to S3 using MultiPart. See Object Key and Metadata for more information.

1let data: Data = Data() // The data to upload
2
3let uploadExpression = AWSS3TransferUtilityMultiPartUploadExpression()
4uploadExpression.setValue("AES256", forRequestHeader: "x-amz-server-side-encryption-customer-algorithm")
5uploadExpression.progressBlock = {(task, progress) in
6 DispatchQueue.main.async(execute: {
7 // Do something e.g. Update a progress bar.
8 })
9}
10
11let transferUtility = AWSS3TransferUtility.default()
12
13transferUtility.uploadUsingMultiPart(
14 data:data,
15 bucket: "S3BucketName",
16 key: "S3UploadKeyName",
17 contentType: "text/plain",
18 expression: uploadExpression,
19 completionHandler: nil
20).continueWith { (task) -> AnyObject! in
21 if let error = task.error {
22 print("Error: \(error.localizedDescription)")
23 }
24
25 return nil;
26}

Working with access levels in your app code

All Amazon S3 resources are private by default. If you want your users to have access to S3 buckets or objects, you can assign appropriate permissions with an IAM policy.

IAM Policy Based Permissions

When you upload objects to the S3 bucket the Amplify CLI creates, you must manually prepend the appropriate access-level information to the key. The correct prefix - public/, protected/ or private/ - will depend on the access level of the object as documented in the Storage Access section.

Transfer Utility and Pre-Signed URLS

The TransferUtility generates Amazon S3 pre-signed URLs to use for background data transfer. The best practice is to use Amazon Cognito for credentials with Transfer Utility. With Amazon Cognito Identity, you receive AWS temporary credentials that are valid for up to 60 minutes. The pre-signed URLs built using these credentials are bound by the same time limit, after which the URLs will expire.

Because of this limitation, when you use TransferUtility with Amazon Cognito, the expiry on the Pre-Signed URLs generated internally is set to 50 minutes. Transfers that run longer than the 50 minutes will fail. If you are transferring a large enough file where this becomes a constraint, you should create static credentials using AWSStaticCredentialsProvider ( see Authentication for more details on how to do this) and increase the expiry time on the Pre-Signed URLs by increasing the value for the timeoutIntervalForResource in the Transfer Utility Options. Note that the max allowed expiry value for a Pre-Signed URL is 7 days.

Additional Resources