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

22

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

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

手动使用HTTP API(例如AFNetworking)会更容易吗?


请检查我的更新答案(新来的人看这个):https://dev59.com/a18e5IYBdhLWcg3wyM0r#38897748 - Lazar Kukolj
1
请注意,我不是在问如何将图像保存到文件中以便上传。问题是关于在不创建文件的情况下上传图像(出于安全原因,在我的情况下,因为用户可以访问文件系统)。 - tothemario
7个回答

19

尽管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
由于某些原因,该前缀未包含在文件路径中 - 您应该尝试使用[NSURL fileURLWithPath:filePath] - SoftDesigner
1
我可以建议,在我的情况下,上传后使用[[NSFileManager defaultManager] removeItemAtPath:filePath error:&error];删除了文件。 - Lukas
当我使用这段代码时,它没有上传整个数据。它只上传了一定量的数据,然后停止并出现错误。有什么想法吗? - kb920
请记得导入<AWSS3TransferManager.h>和<AWSCore.h>。此外,我还需要将BFExectutor更改为AWSExecutor,BFTask更改为AWSTask。非常感谢Rick的回答,帮了我很多! - Tim
这是我的工作 - uploadRequest.ACL = AWSS3BucketCannedACLPublicRead; - Roei Nadam
显示剩余2条评论

11

看起来AWSiOSSDKv2目前不支持从内存上传图片 :(

来自Github的问题

只接受NSURL文件的决定是基于以下几个原因:

  1. 自版本1以来,暂停/恢复功能需要输入为文件。当应用程序被终止时,无法恢复NSData并重试传输。
  2. iOS 7及以上版本上的后台传输仅支持文件。目前,我们不支持后台传输,但我们计划在未来支持它。我们考虑接受NSData,并将数据内部保存到临时目录中。
  3. 我们决定不在2.0版本中包含此功能,因为如果NSData由文件支持,它会使数据的磁盘使用量加倍。此外,开发人员在使用S3TransferManager时必须处理与磁盘相关的错误。即使我们决定不在2.0版本中接受NSData,我们仍然期待您的反馈。如果这是您希望在将来版本中看到的功能,请创建一个新问题,请求该功能。

```


感谢您更新...我对此很好奇。 - jplego
如果我想使用V2上传视频,那么我需要将NSData写入文件并上传吗? - Mr.G

3
您可以使用“预签名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;

  }];
}

文档在S3 v2 SDK文档中有记录,网址为http://docs.aws.amazon.com/mobile/sdkforios/developerguide/s3transfermanager.html#use-pre-signed-urls-to-transfer-objects-in-the-background。虽然嵌套的完成块有些混乱,但要点是您请求一个URL,然后当返回时开始上传任务。这是用于原型测试的代码,不是完美的代码。您应该检查上传的状态码而不仅仅是错误。

2
在当前版本的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;
       }
}

2

这是一篇更新过的答案,让人们不必自己摸索(就像我一样):D

导入正确的文件(在此处下载:此处

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

我使用了一个按钮来确定用户何时想要上传文件。
- (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;
                                        }];
}

有用的链接:

AWS文档

YouTube视频

希望这些能帮助到某些人!


你还没有把它保存到文件里吗? - Marin

0

嗨,你可以在iPhone上使用Amazon iOS v2,无需将图像保存到临时文件夹中即可发送图像。

在这段代码中,logFile.bodyNSData类型。

这段代码会帮助你的朋友。

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

即使在v2中,body属性仍然是NSURL,你不能将其传递给NSData。 - user3344977

0

使用AWSS3TransferUtility,您可以上传任何数据类型,现在AWSS3TransferManagerUploadRequest已经过时,这里是上传jpeg的代码示例,但可以转换为任何数据类型:

代码示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接