0

I'm trying to upload a large (like in don't fit in memory) file to S3 using presigned request. I finally got it to work with curl

curl -v -T video.mp4 "http://<myBucket>.s3.amazonaws.com/video.mp4?AWSAccessKeyId=<myAccessKey>&Expires=1492187347&Signature=vpcUnvGALlVXju31Qk2nXNmBTgc%3D"

I'm trying to now do that from my app. I first tried with AF (that I'd rather not use):

let videoPath = Bundle.main.path(forResource: "media", ofType: "mov")!
let videoUrl = URL(fileURLWithPath: videoPath)
let presignedUrl = "http://<myBucket>.s3.amazonaws.com/video.mp4?AWSAccessKeyId=<myAccessKey>&Expires=1492187347&Signature=vpcUnvGALlVXju31Qk2nXNmBTgc%3D"
request = Alamofire.upload(videoUrl, to: presignedUrl, method: .put, headers: [:])
request.responseString(completionHandler: { response in
    print(response)
})
request.resume()

Which prints an successful response that is actually an error:

The request signature we calculated does not match the signature you provided. Check your key and signing method.

I've read in a few places that there might be issues with headers, and I'd rather not use AF here anyway.

What is in Swift an equivalent of curl -T filePath url ?

Guig
  • 9,891
  • 7
  • 64
  • 126

1 Answers1

1

When signing the request, pass the content type. In node:

var AWS = require('aws-sdk');

AWS.config.update({
  accessKeyId: accessKeyId,
  secretAccessKey: secretAccessKey,
  region: 'us-east-1',
});

var s3 = new AWS.S3();

var params = {
  Bucket: bucketName,
  Key: 'path/my-video.mp4',  
  Expires: 1800000,
  ContentType: 'video/mp4',
};
var signedUrl = s3.getSignedUrl('putObject', params)
// returns something like:
// https://<- my bucket ->.s3.amazonaws.com/path/my-video.mp4?AWSAccessKeyId=<- my access key ->&Content-Type=video%2Fmp4&Expires=1492203291&Signature=HeRBUObYQiQ6FIH%2Fg%2FOI0qv57VY%3D

and now in swift:

let request = NSMutableURLRequest(url: presignedUrl)
request.cachePolicy = NSURLRequest.CachePolicy.reloadIgnoringCacheData
request.httpMethod = "PUT"
request.setValue("video/mp4", forHTTPHeaderField: "Content-Type")
request.setValue("<- my bucket ->.s3.amazonaws.com", forHTTPHeaderField: "host")
let uploadTask = session.uploadTask(with: request as URLRequest, fromFile: videoUrl) { data, response, error in
  ...
}
uploadTask.resume()
Guig
  • 9,891
  • 7
  • 64
  • 126