tothemario
tothemario

Reputation: 6309

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)?

Upvotes: 22

Views: 20629

Answers (7)

Saeed Ir
Saeed Ir

Reputation: 2332

With AWSS3TransferUtility you can upload any data type, also AWSS3TransferManagerUploadRequest is deprecated now, here is the code sample to upload jpeg but can be converted for any data type:

Code sample

Upvotes: 0

Alper
Alper

Reputation: 3993

In the current version of the SDK you can use AWSS3TransferUtility and then it does everything for you.

func uploadData() {

   let data: Data = Data() // Data to be uploaded

   let expression = AWSS3TransferUtilityUploadExpression()
      expression.progressBlock = {(task, progress) in
         DispatchQueue.main.async(execute: {
           // Do something e.g. Update a progress bar.
        })
   }

   var completionHandler: AWSS3TransferUtilityUploadCompletionHandlerBlock?
   completionHandler = { (task, error) -> Void in
      DispatchQueue.main.async(execute: {
         // Do something e.g. Alert a user for transfer completion.
         // On failed uploads, `error` contains the error object.
      })
   }

   let transferUtility = AWSS3TransferUtility.default()

   transferUtility.uploadData(data,
        bucket: "YourBucket",
        key: "YourFileName",
        contentType: "text/plain",
        expression: expression,
        completionHandler: completionHandler).continueWith {
           (task) -> AnyObject! in
               if let error = task.error {
                  print("Error: \(error.localizedDescription)")
               }

               if let _ = task.result {
                  // Do something with uploadTask.
               }
               return nil;
       }
}

Upvotes: 2

Lazar Kukolj
Lazar Kukolj

Reputation: 706

This is an updated answer so people don't have to figure it out themselves (like me) :D

Import the proper files (download it here)

#import <AWSCore/AWSCore.h>
#import <AWSS3TransferManager.h>

.m

- (void)viewDidLoad {
    [super viewDidLoad];

    AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1
       identityPoolId:@"us-east-1:*******-******-*****-*****-*****"];

    AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1
                                                                     credentialsProvider:credentialsProvider];

    AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
}

I used a button to know when the user wants to upload the file

- (void)upload{
    
    //convert uiimage to
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:@".png"]];
    [UIImagePNGRepresentation(YOUR_UIIMAGE) writeToFile:filePath atomically:YES];
    
    NSURL* fileUrl = [NSURL fileURLWithPath:filePath];
    
    //upload the image
    AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
    uploadRequest.body = fileUrl;
    uploadRequest.bucket = @"YOUR_BUCKET_NAME";
    uploadRequest.key = @"YOUR_FOLDER_NAME (if you have one)/NEW_IMAGE_NAME.png";
    uploadRequest.contentType = @"image/png";
    uploadRequest.ACL = AWSS3BucketCannedACLPublicRead;
    
    AWSS3TransferManager *transferManager = [AWSS3TransferManager defaultS3TransferManager];
    
    [[transferManager upload:uploadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor]
                                                       withBlock:^id(AWSTask *task) {
                                        if (task.error != nil) {
                                            NSLog(@"%s %@","Error uploading :", uploadRequest.key);
                                        }else { NSLog(@"Upload completed"); }
                                            return nil;
                                        }];
}

Helpfull links:

AWS Documnetion

YouTube Video

Hopefully this helps someone out!

Upvotes: 2

Brian Broom
Brian Broom

Reputation: 497

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.

Upvotes: 3

Rick
Rick

Reputation: 389

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;
}];

Upvotes: 19

Oleg Gordiichuk
Oleg Gordiichuk

Reputation: 15512

Hi you can sand image without saving image to the temporary folder in the iPhone Amazon iOS v2 gives such option as well.

In this code logFile.body it is NSData.

This code will help you my friend.

AWSS3PutObjectRequest *logFile = [AWSS3PutObjectRequest new];
  logFile.bucket = uploadTokenData_.bucket;
  logFile.key = key;
  logFile.contentType = contentType;
  logFile.body = data_;
  logFile.contentLength = [NSNumber numberWithInteger:[data_ length]];

AWSS3 *S3 = [[AWSS3 alloc] initWithConfiguration:[AWSCredentialsProvider runServiceWithStsCredential]];

AWSS3TransferManager *transferManager = [[AWSS3TransferManager alloc] initWithS3:S3];

[[transferManager.s3 putObject:logFile] continueWithBlock:^id(BFTask *task)
{

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

Upvotes: 0

tothemario
tothemario

Reputation: 6309

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.

```

Upvotes: 11

Related Questions