S3 video upload fails with AWS sdk v2 using Swift

时间:2015-07-28 22:39:11

标签: ios swift amazon-web-services amazon-s3

I am trying to upload video files to S3 using the latest (v2.2.2) AWS SDK. It works perfectly on WiFi but for some unknown reason it stalls on 3G every time at the same place. Looking at the verbose debug output it seems like it gets to 5 chunks each time which seems oddly consistent.

My upload code is:

let credentialsProvider = CustomAmazonCredentialsProvider(accessKey: json["credentials"]["key"].stringValue, secretKey: json["credentials"]["secret"].stringValue, sessionKey: json["credentials"]["token"].stringValue)

            let configuration : AWSServiceConfiguration = AWSServiceConfiguration(region: AWSRegionType.EUWest1, credentialsProvider: credentialsProvider)
            configuration.maxRetryCount = 10;



            AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = configuration

            let filePath : String! = json["file_path"].stringValue

            AWSS3.registerS3WithConfiguration(configuration, forKey: filePath)

            AWSS3TransferManager.registerS3TransferManagerWithConfiguration(configuration, forKey: filePath)

            let uploadRequest : AWSS3TransferManagerUploadRequest = AWSS3TransferManagerUploadRequest()

            let file : String = json["file_path"].stringValue

            uploadRequest.body = NSURL(fileURLWithPath: self.videoPath!) //self.moviePlayer!.contentURL
            uploadRequest.key = file
            uploadRequest.bucket = json["bucket"].stringValue

            self.uploadRequests.append(uploadRequest)

            self.transferManager  = AWSS3TransferManager.S3TransferManagerForKey(json["file_path"].stringValue)


            uploadRequest.uploadProgress = {[unowned self](bytesSent:Int64,
                totalBytesSent:Int64, totalBytesExpectedToSend:Int64) in

                dispatch_sync(dispatch_get_main_queue(), { () -> Void in

                    var progress : CGFloat = CGFloat(totalBytesSent) / CGFloat(totalBytesExpectedToSend)
                    println("prog \(progress)")

                    println("total \(totalBytesSent)")
                    println("total \(totalBytesExpectedToSend)")

                    if ( progress < 1.0 ) {
                        SVProgressHUD.showProgress(Float((CGFloat(totalBytesSent) / CGFloat(totalBytesExpectedToSend))), status: "Uploading", maskType: SVProgressHUDMaskType.None)


                    }
                })

            }



            self.transferManager!.upload(uploadRequest).continueWithBlock({ (task) -> AnyObject! in
            ...
          })

I'm pretty sure I have references to everything and as I said, it works perfectly on WiFi so why would it fail consistently at 5 chunks on mobile network?

UPDATE:

Here is some debug output:

2015-07-29 10:34:55.938 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:653 | -[AWSS3ChunkedEncodingInputStream nextChunk] | stream read: 32677, chunk size: 32768
2015-07-29 10:34:55.947 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:669 | -[AWSS3ChunkedEncodingInputStream getSignedChunk:] | AWS4 String to Sign: [AWS4-HMAC-SHA256-PAYLOAD
20150729T093445Z
20150729/eu-west-1/s3/aws4_request
b1553d25ed783a533cd12380d9df4354f6d331d646692d4401b802cc30348502
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
70d7035aabde0212f688146957761f57dc68cab99dddb31ec0878aea6d222292]
prog 0.0495226365596895
total 98031
total 1979519
2015-07-29 10:34:55.951 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:675 | -[AWSS3ChunkedEncodingInputStream getSignedChunk:] | AWS4 Chunked Header: [007fa5;chunk-signature=4dc07dfbf557576f4a3fcb0bd976a0a24a4202e8bc19605b65447d5ac5839909

]
2015-07-29 10:34:55.952 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:653 | -[AWSS3ChunkedEncodingInputStream nextChunk] | stream read: 32677, chunk size: 32768
prog 0.066030182079586
total 130708
total 1979519
2015-07-29 10:35:01.379 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:669 | -[AWSS3ChunkedEncodingInputStream getSignedChunk:] | AWS4 String to Sign: [AWS4-HMAC-SHA256-PAYLOAD
20150729T093445Z
20150729/eu-west-1/s3/aws4_request
4dc07dfbf557576f4a3fcb0bd976a0a24a4202e8bc19605b65447d5ac5839909
e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
c90c101be69ea0bac00649e48734dc4b23c3aa81a914d71961f810a1d98fc640]
2015-07-29 10:35:01.381 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:675 | -[AWSS3ChunkedEncodingInputStream getSignedChunk:] | AWS4 Chunked Header: [007fa5;chunk-signature=ecae7e66fbd9337aaf1063be3f8818293255ddc161e89c3fda929aecedb5d4f7

]
2015-07-29 10:35:01.382 Constent[6229:586874] AWSiOSSDKv2 [Debug] AWSSignature.m line:653 | -[AWSS3ChunkedEncodingInputStream nextChunk] | stream read: 32677, chunk size: 32768
prog 0.0825377275994825
total 163385
total 1979519

I still don't understand why it would stick on 5 x 32k chunks so reliably.

1 个答案:

答案 0 :(得分:0)

It is possible that some concurrency issue is happening. Because you are using dispatch_sync for dispatch_get_main_queue(), if anything is blocked inside of the block, the queue that manages networking delegate calls can get deadlocked. You should make sure nothing is blocking in the block. Also, I would change dispatch_sync to dispatch_async.

There are a few unrelated issues in your code snippet. You seem to be instantiating a new service client for every request. This is very inefficient. You should create one instance of the service client and reuse it as much as possible.

Also, you should carefully review the implementation of CustomAmazonCredentialsProvider. AWS temporary credentials do expire, and when it happens, your credentials provider should respond to - refresh so that the SDK can retrieve a fresh set of credentials. Implement your credentials provider by conforming to AWSCredentialsProvider. Take a look at the implementations of AWSWebIdentityCredentialsProvider and AWSCognitoCredentialsProvider as examples. This credentials provider should:

  • Retrieve the access key, secret key, and session key from your server.
  • Persist them locally until they expire.
  • Return the credentials when requested.
  • Re-retrieve them from your server if they are expired.
  • Initiate the credentials refreshing process when - refresh is called.

I encourage you to take a look at Amazon Cognito Identity. With Amazon Cognito, you can create unique end user identifiers for accessing AWS cloud services by using public login providers such as Amazon, Facebook, Google, and any OpenID Connect compatible provider, or by using your own user identity system. It covers many of TVM use cases, and it is easier to use and manage.