如何使用AWS iOS SDK v2将UIImage上传到S3

时间:2022-01-10 23:06:04

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:

Github中的README页面(https://github.com/aws/aws-sdk-ios-v2)已经有一个从文件路径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?

但是,如果我在内存中只有UIImage(没有文件路径)怎么办?是否可以使用SDK将UIImage(或它的NSData)上传到S3?

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

手动使用HTTP API(使用AFNetworking之类的东西)会更容易吗?

6 个解决方案

#1


19  

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

尽管AWSiOSSDKv2不支持从内存中上传图像,但您可以将其另存为文件然后上传。

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

#2


11  

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

似乎AWSiOSSDKv2目前不支持从内存上传图像:(

From a Github issue:

来自Github问题:

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

仅接受NSURL文件的决定受以下因素驱动:

  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. 从v1开始,暂停/恢复功能要求输入为文件。当应用程序被杀死时,无法恢复NSData并重试传输。
  3. 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.
  4. iOS 7及更高版本的后台传输仅支持文件。目前,我们不支持后台传输,但我们计划在未来支持它。我们考虑接受NSData并在内部将数据持久化到临时目录。
  5. 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.
  6. 我们决定不在2.0版本中包含它,因为如果NSData由文件支持,它会使数据的磁盘使用量翻倍。此外,开发人员在使用S3TransferManager时必须处理与磁盘相关的错误。即使我们决定在2.0版本中不接受NSData,我们也会对您的反馈持开放态度。如果这是您希望在将来的版本中看到的功能,请使用功能请求创建新问题。

```

```

#3


2  

You can apparently do it with "presigned URLs"

你可以用“预先签名的URL”来做到这一点

- (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

在http://docs.aws.amazon.com/mobile/sdkforios/developerguide/s3transfermanager.html#use-pre-signed-urls-to-transfer-objects-in-the-background的v2 SDK的S3文档中进行了记录

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.

它有点乱七八糟的嵌套完成块,但要点是你请求一个网址,然后当它返回时你开始上传任务。这是原型测试,而不是抛光代码。您应该检查上传的状态代码而不仅仅是错误。

#4


2  

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

.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

AWS Documnetion

YouTube Video

YouTube视频

Hopefully this helps someone out!

#5


0  

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

嗨,您可以在不将图像保存到iPhone中的临时文件夹的情况下打磨图像亚马逊iOS v2也提供了这样的选项。

In this code logFile.body it is NSData.

在这段代码中,logFile.body是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");
  }
}

#6


0  

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

在当前版本的SDK中,您可以使用AWSS3TransferUtility,然后它会为您完成所有操作。

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

#1


19  

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

尽管AWSiOSSDKv2不支持从内存中上传图像,但您可以将其另存为文件然后上传。

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

#2


11  

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

似乎AWSiOSSDKv2目前不支持从内存上传图像:(

From a Github issue:

来自Github问题:

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

仅接受NSURL文件的决定受以下因素驱动:

  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. 从v1开始,暂停/恢复功能要求输入为文件。当应用程序被杀死时,无法恢复NSData并重试传输。
  3. 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.
  4. iOS 7及更高版本的后台传输仅支持文件。目前,我们不支持后台传输,但我们计划在未来支持它。我们考虑接受NSData并在内部将数据持久化到临时目录。
  5. 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.
  6. 我们决定不在2.0版本中包含它,因为如果NSData由文件支持,它会使数据的磁盘使用量翻倍。此外,开发人员在使用S3TransferManager时必须处理与磁盘相关的错误。即使我们决定在2.0版本中不接受NSData,我们也会对您的反馈持开放态度。如果这是您希望在将来的版本中看到的功能,请使用功能请求创建新问题。

```

```

#3


2  

You can apparently do it with "presigned URLs"

你可以用“预先签名的URL”来做到这一点

- (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

在http://docs.aws.amazon.com/mobile/sdkforios/developerguide/s3transfermanager.html#use-pre-signed-urls-to-transfer-objects-in-the-background的v2 SDK的S3文档中进行了记录

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.

它有点乱七八糟的嵌套完成块,但要点是你请求一个网址,然后当它返回时你开始上传任务。这是原型测试,而不是抛光代码。您应该检查上传的状态代码而不仅仅是错误。

#4


2  

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

.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

AWS Documnetion

YouTube Video

YouTube视频

Hopefully this helps someone out!

#5


0  

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

嗨,您可以在不将图像保存到iPhone中的临时文件夹的情况下打磨图像亚马逊iOS v2也提供了这样的选项。

In this code logFile.body it is NSData.

在这段代码中,logFile.body是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");
  }
}

#6


0  

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

在当前版本的SDK中,您可以使用AWSS3TransferUtility,然后它会为您完成所有操作。

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