Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload a UIImage to S3 with AWS iOS SDK v2

The README page in Github (https://github.com/aws/aws-sdk-ios-v2) already has an example to upload an image, from the file path URL:

AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.bucket = yourBucket;
uploadRequest.key = yourKey;
uploadRequest.body = yourDataURL; // <<<< this is a NSURL
uploadRequest.contentLength = [NSNumber numberWithUnsignedLongLong:fileSize];

But, what if I only have a UIImage in memory (without file path)? Is it possible to upload a UIImage (or it's NSData) to S3 using the SDK?

Would it be easier to manually use the HTTP API (using something like AFNetworking)?

like image 333
tothemario Avatar asked Aug 18 '14 18:08

tothemario


3 Answers

Even though AWSiOSSDKv2 doesn't support uploading images from memory, you can save it as a file and then upload it.

//image you want to upload
UIImage* imageToUpload = [UIImage imageNamed:@"imagetoupload"]; 

//convert uiimage to 
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", dateKey]];
[UIImagePNGRepresentation(imageToUpload) writeToFile:filePath atomically:YES];

NSURL* fileUrl = [NSURL fileURLWithPath:filePath];

//upload the image
AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.body = fileUrl;
uploadRequest.bucket = AWS_BUCKET_NAME;
uploadRequest.key = @"yourkey";
uploadRequest.contentType = @"image/png";
[[transferManager upload:thumbNailUploadRequest] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
    if(task.error == nil) {
        NSLog(@"woot");
    }
    return nil;
}];
like image 191
Rick Avatar answered Oct 21 '22 10:10

Rick


It seems that AWSiOSSDKv2 don't have support to upload images from memory at this moment :(

From a Github issue:

The decision to accept only file NSURLs was driven by the following factors:

  1. Since v1, the pause / resume features require the input to be files. It's not possible to recover NSData and retry the transfer when the app is killed.
  2. The background transfer on iOS 7 and above only supports files. Currently, we don't support background transfer, but we are planning to support it in the future. We considered accepting an NSData and internally persisting the data to a temporary directory.
  3. We decided not to include this in 2.0 release because if the NSData is backed by a file, it doubles the disk usage for the data. Also, developers have to deal with disk related errors when using S3TransferManager. Even though we decided not to accept NSData in 2.0 release, we are open for your feedback. If this is a feature you want to see in the future release, please create a new issue with the feature request.

```

like image 45
tothemario Avatar answered Oct 21 '22 12:10

tothemario


You can apparently do it with "presigned URLs"

- (void)uploadImageToS3: (UIImage *)image {
  NSData *imageData = UIImageJPEGRepresentation(image, 0.7);

  AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
  getPreSignedURLRequest.bucket = @"bucket-name";
  getPreSignedURLRequest.key = @"image-name.jpg";
  getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodPUT;
  getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];

  NSString *fileContentTypeString = @"text/plain";
  getPreSignedURLRequest.contentType = fileContentTypeString;

  [[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:getPreSignedURLRequest] continueWithBlock:^id(AWSTask *task) {

    if (task.error) {
      NSLog(@"Error: %@", task.error);
    } else {

      NSURL *presignedURL = task.result;
      NSLog(@"upload presignedURL is \n%@", presignedURL);

      NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:presignedURL];
      request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
      [request setHTTPMethod:@"PUT"];
      [request setValue:fileContentTypeString forHTTPHeaderField:@"Content-Type"];

      NSURLSessionUploadTask *uploadTask = [[NSURLSession sharedSession] uploadTaskWithRequest:request fromData:imageData completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

        if (error) {
          NSLog(@"Upload errer: %@", error);
        }
        NSLog(@"Done");
      }];

      [uploadTask resume];
    }

    return nil;

  }];
}

Documented in the S3 docs for v2 SDK at http://docs.aws.amazon.com/mobile/sdkforios/developerguide/s3transfermanager.html#use-pre-signed-urls-to-transfer-objects-in-the-background

Its a bit of a mess with nested completion blocks, but the gist is you request a url, then when that returns you start an upload task. This was for a prototype test, not polished code. You should check the status code on the upload instead of just the error.

like image 3
Brian Broom Avatar answered Oct 21 '22 11:10

Brian Broom