I'm trying to upload file on S3 bucket and device is getting access information from another server (AWSAccessKeyId and Signature). Is it possible to upload file with AWS iOS SDK v2? If not are there any chances to use another approach possible for iOS (eg. generate Pre-Signed URL and do the http post/put)?
Right now I'm using this approach, but it's for access_key/access_secret:
AWSStaticCredentialsProvider *credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:awsAccessKey secretKey:awsSecretKey];
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
AWSS3 *transferManager = [[AWSS3 alloc] initWithConfiguration:configuration];
AWSS3PutObjectRequest *getLog = [[AWSS3PutObjectRequest alloc] init];
getLog.bucket = awsS3Bucket;
getLog.key = awsS3FileNameString;
getLog.contentType = #"text/plain";
NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:logFileName];
long long fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:fileName error:nil][NSFileSize] longLongValue];
getLog.body = [NSURL fileURLWithPath:fileName];
getLog.contentLength = [NSNumber numberWithUnsignedLongLong:fileSize];
[[transferManager putObject:getLog] continueWithBlock:^id(BFTask *task) {
if(task.error)
{
NSLog(#"Error: %#",task.error);
}
else
{
NSLog(#"Got here: %#", task.result);
}
return nil;
}];
I'll be grateful for any ideas.
I recommend the following approach:
Generate the access key, secret key, and session token on your server. You have many language options including Java, .NET, PHP, Ruby, Python, and Node.js.
Implement your own credentials provider by conforming to AWSCredentialsProvider. This credentials provider should:
Retrieve the access key, secret key, and session key from your server.
Persist them until they expire.
Return the credentials when requested.
If they are expired, re-retrieve them from your server.
Calling refresh also should initiate the credentials retrieval process.
Assign your credentials provider to defaultServiceConfiguration or pass it to initWithConfiguration:.
As a side note, when using initWithConfiguration:, you need to manually retain a strong reference to an instance of AWSS3. Using defaultS3 will eliminate the need for this.
Hope this helps,
Related
I am working on an app in which I need to upload image using AWSS3. Below is my code. However I am getting error something about pool id not found. I am not sure whats going, Do I need to add anything more.
This is the error getting
Error Domain=com.amazonaws.AWSCognitoIdentityErrorDomain Code=10 "(null)" UserInfo={__type=ResourceNotFoundException, message=IdentityPool 'ap-northeast-1:xxxxxxx' not found
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#""];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
// get the image from a UIImageView that is displaying the selected Image
// create a local image that we can use to upload to s3
NSString *path = [NSTemporaryDirectory() stringByAppendingPathComponent:#"image.png"];
NSData *imageData = UIImagePNGRepresentation(selectedImage);
[imageData writeToFile:path atomically:YES];
// once the image is saved we can use the path to create a local fileurl
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
// next we set up the S3 upload request manager
AWSS3TransferManagerUploadRequest *_uploadRequest = [AWSS3TransferManagerUploadRequest new];
// set the bucket
_uploadRequest.bucket = #"chatify";
// I want this image to be public to anyone to view it so I'm setting it to Public Read
_uploadRequest.ACL = AWSS3ObjectCannedACLPublicRead;
// set the image's name that will be used on the s3 server. I am also creating a folder to place the image in
_uploadRequest.key = #"ios/image.png";
// set the content type
_uploadRequest.contentType = #"image/png";
// we will track progress through an AWSNetworkingUploadProgressBlock
_uploadRequest.body = url;
__weak ClaimViewController *weakSelf = self;
_uploadRequest.uploadProgress =^(int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend){
dispatch_sync(dispatch_get_main_queue(), ^{
weakSelf.amountUploaded = totalBytesSent;
weakSelf.filesize = totalBytesExpectedToSend;
[weakSelf update];
});
};
// now the upload request is set up we can creat the transfermanger, the credentials are already set up in the app delegate
AWSS3TransferManager *transferManager = [AWSS3TransferManager defaultS3TransferManager];
// start the upload
[[transferManager upload:_uploadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor] withBlock:^id(AWSTask *task) {
// once the uploadmanager finishes check if there were any errors
if (task.error) {
NSLog(#"%#", task.error);
}else{// if there aren't any then the image is uploaded!
// this is the url of the image we just uploaded
NSLog(#"https://s3.amazonaws.com/s3-demo-objectivec/foldername/image.png");
}
return nil;
}];
hi #kashif the error clear says that you need to add the pool id
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#""];
but in the Above code you just pass the Black in identityPoolId
Here's also another method to which provide Credential
AWSStaticCredentialsProvider *credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:#"AccessKey" secretKey :#"secretKey"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSWest2
credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
This error is because of your Cognito Identity Pool being deleted from your account or you setting the region incorrectly. Your region should be set to
AWSRegionAPNorthEast1 based on what I see in the posted code.
Thanks,
Rohan
The documentation for AWS (Amazon Web Services) is vast, and rather chaotic. I found a git repo for iOS that demonstrates performing various tasks including a couple of projects for getting/putting data to S3.
I already have an existing client application that uses Apples NSURLSession to do HTTPS GET requests for content from S3 when the records are public. However, my client want the data to be secure.
I don't want to invest the time to learn the AWSS3TransferManager framework, and I don't want to include the whole framework in my project, either). I don't want all that baggage. All I need is a call that will let me provide a user ID or access key/secret key, or perhaps a password, and get a query string I can add to my HTTP get requests that authorizes the request for all users of the app.
I don't need upload. I don't need API-based console support. I don't need or want Amazon's session manager/download manager. All I want is the 1 call that will let me provide an access key and secret key and get back something I can add to the get request that authorizes it, so I can submit a request and get back a file. Should be easy, right? I've been pouring through the documentation and sample code for a couple of hours now, and no luck.
Looking at the samples, they use 500 kilos of frameworks I don't need and don't want.
Can somebody point me to a minimal library that lets me generate the key(s) I need to prove that the user has access to this content, given username and an access key Id and secret key, or perhaps a password? This would not be that hard.
Here is simple code for HTTP GET. If you are interested in HTTP PUT - check implementation at http://simpleios.s3-website-us-east-1.amazonaws.com.
#import <CommonCrypto/CommonCrypto.h>
static NSString * const accessKey=#"AKIAIOSFODNN7EXAMPLE";
static NSString * const secretKey=#"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
...
NSString * fileUrl=#"https://s3.amazonaws.com/someBucket/someFile";
NSString * bucket=#"someBucket";
NSString * filePath=#"someFile";
NSURLSession *delegateFreeSession=[NSURLSession sessionWithConfiguration: [NSURLSessionConfiguration defaultSessionConfiguration] delegate: nil delegateQueue: [NSOperationQueue mainQueue]];
[[delegateFreeSession dataTaskWithURL: [NSURL URLWithString:[fileUrl stringByAppendingString:[self buildQuery:bucket forFile:filePath]]] completionHandler:^(NSData *received, NSURLResponse *response, NSError *error) {
if(error!=nil ){
NSLog(#"Error trying to download file - %#", error);
}
else{
//do something
}
}] resume ];
...
- (NSString * ) buildQuery:(NSString *)bucketName forFile:(NSString *)filePath{
NSString *expDate=[self expirationDate];
NSMutableString *base=[NSMutableString new];
[base appendString:#"?AWSAccessKeyId="];
[base appendString:accessKey];
[base appendString:#"&Expires="];
[base appendString:expDate];
[base appendString:#"&Signature="];
NSMutableString *stringToSign=[NSMutableString new];
[stringToSign appendString:#"GET\n\n\n" ];
[stringToSign appendString:expDate];
[stringToSign appendString:#"\n" ];
[stringToSign appendString:#"/"];
[stringToSign appendString:bucketName];
[stringToSign appendString:#"/"];
[stringToSign appendString:filePath];
NSString *signature=[self sign:stringToSign];
signature=[signature stringByReplacingOccurrencesOfString:#"/" withString:#"%2F"];
signature=[signature stringByReplacingOccurrencesOfString:#"+" withString:#"%2B"];
signature=[signature stringByReplacingOccurrencesOfString:#"=" withString:#"%3D"];
[base appendString:signature];
return base;
}
- (NSString *) expirationDate{
int expireInSeconds=300;
double sec=[[NSDate date] timeIntervalSince1970]+expireInSeconds;
static NSNumberFormatter *numberFormatter=nil;
if(numberFormatter==nil){
numberFormatter=[NSNumberFormatter new];
[numberFormatter setMaximumFractionDigits:0];
}
NSString *expDate=[numberFormatter stringFromNumber:[NSNumber numberWithDouble:sec]];
return expDate;
}
- (NSString *)sign:(NSString *)stringToSign {
NSData *data=[stringToSign dataUsingEncoding:NSUTF8StringEncoding];
CCHmacContext context;
const char *keyCString = [secretKey cStringUsingEncoding:NSASCIIStringEncoding];
CCHmacInit(&context, kCCHmacAlgSHA1, keyCString, strlen(keyCString));
CCHmacUpdate(&context, [data bytes], [data length]);
unsigned char digestRaw[CC_SHA1_DIGEST_LENGTH];
NSInteger digestLength = CC_SHA1_DIGEST_LENGTH;
CCHmacFinal(&context, digestRaw);
NSData *digestData = [NSData dataWithBytes:digestRaw length:digestLength];
return [digestData base64EncodedStringWithOptions:kNilOptions];
}
One problem with this is that it sounds like you are trying to embed secret/access keys into the app itself instead of using something more secure like Amazon Cognito https://aws.amazon.com/cognito/, which the iOS SDK provides access to.
The other is that iOS SDK for AWS doesn't allow you to cherry pick which methods to include, though it does allow you to generate a pre-signed URL for S3 gets. For just S3 you only need the S3 & Core frameworks, but it sounds like that is too much as well.
If you really just want the most minimal download size possible you could implement AWS signing yourself by looking at the AWS iOS SDK source code https://github.com/aws/aws-sdk-ios or S3's signing documentation, but signing an AWS request is non-trivial, and you would lose a lot of beneficial logic (retries, ect...).
You also mentioned not wanting to learn the TransferManager. The TransferUtility is the successor of that and would be drastically faster to learn than implementing your own signing. I can't see it taking more than an hour or two to understand it (maybe a little more if you decide to use Cognito for authentication, but that's really tangential to TransferUtility). http://docs.aws.amazon.com/mobile/sdkforios/developerguide/s3transferutility.html
Edit: In order to give credentials for the PreSignedURLBuilder. You can also see adding credentials to all clients created with the SDK at http://docs.aws.amazon.com/mobile/sdkforios/developerguide/setup.html (Getting started with Swift/Objective-c section)
You can use this to provide credentials
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc]
initWithRegionType:AWSRegionUSEast1 identityPoolId:#"IDENTITY_POOL_ID"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc]
initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
[AWSS3PreSignedURLBuilder registerS3PreSignedURLBuilderWithConfiguration:configuration
forKey:#"customServiceConfiguration"];
AWSS3PreSignedURLBuilder *customPreSignedURLBuilder = [AWSS3PreSignedURLBuilder S3PreSignedURLBuilderForKey:#"customServiceConfiguration"];
I am new to using AWS as a backend for my iOS mobile app. I have set up all the roles and identiy pool needed. I have added the following code to my AppDelegate.m file:
// Starting AWS
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#"IdentityPool"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
What is the next step to see if I am authenticated or not? Trying to figue out iOS code to see what role I am in.
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
Import service headers where you want to use AWS and then just try to make a call to AWS services.
AWSS3Transfermanager *transferManager = [AWSS3Transfermanager defaultS3TransferManager];
AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.bucket = yourBucket;
uploadRequest.key = yourKey;
uploadRequest.body = yourDataURL;
uploadRequest.contentLength = [NSNumber numberWithUnsignedLongLong:fileSize];
[[transferManager upload:uploadRequest] continueWithBlock:^id(AWSTask *task) {
// Do something with the response
return nil;
}];
Have problem accessing file using presigned url generated by aws sdk, given all necessary permission for the bucket.
I have downloaded the sample code from there github and changed the below
Awscredential provider as per my requirement.
The changes are below
AWSStaticCredentialsProvider *credentialsProvider =[[AWSStaticCredentialsProvider alloc] initWithAccessKey:S3AccessKey secretKey:S3secretKey];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
Even though i upload the file sucessfully to aws I am not able to access them using the presigned url which i get from the aws sdk while uploading.
Can anyone point out the things i am missing in order to access the files using presigned url.
when url is loaded in browser SignatureDoesNotMatch error is shown
The most possible reason for "SignatureDoesNotMatch" is the contents in header-field are different from the one provided while generating the presigned url.
Here is a code snippet to demonstrate how to generate and download a file by using presigned url:
AWSS3GetPreSignedURLRequest *getPreSignedURLRequest = [AWSS3GetPreSignedURLRequest new];
getPreSignedURLRequest.bucket = #"bucketname";
getPreSignedURLRequest.key = #"keyname";
getPreSignedURLRequest.HTTPMethod = AWSHTTPMethodGET;
getPreSignedURLRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];
AWSS3PreSignedURLBuilder *preSignedURLBuilder = [AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder];
[[[preSignedURLBuilder getPreSignedURL:getPreSignedURLRequest] continueWithBlock:^id(BFTask *task) {
if (task.error) {
XCTAssertNil(task.error);
return nil;
}
NSURL *presignedURL = task.result;
//NSLog(#"(GET)presigned URL is: %#",presignedURL.absoluteString);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:presignedURL];
request.cachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
NSError *returnError = nil;
NSHTTPURLResponse *returnResponse = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&returnResponse error:&returnError];
return nil;
}] waitUntilFinished];
The problem is with presigned url encoding. In iOS and android sdks (not in windows) you need to encode the query string in the Presigned url again.
NEWURL=baseurl +(encoded query String);
NEWURL is the correct url you can access.
it worked for me.
Try with this, you need to register PreSignedURL builder.
AWSStaticCredentialsProvider *credentialsProvider =[[AWSStaticCredentialsProvider alloc] initWithAccessKey:S3AccessKey secretKey:S3secretKey];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
// Register S3 PreSignedURL Builder
[AWSS3PreSignedURLBuilder registerS3PreSignedURLBuilderWithConfiguration:configuration forKey:#"configuration_name"];
AWSS3PreSignedURLBuilder * urlBuilder = [AWSS3PreSignedURLBuilder S3PreSignedURLBuilderForKey:#"configuration_name"];
I am trying to upload images to the S3, and for files that are less than 2 mb, it is ok, but for more than 2 mb, server return Code=-1001 "The request timed out.". Could someone explain how it is possible to handle this problem?
Code example below:
AWSS3 *s3 = [[AWSS3 alloc] initWithConfiguration:configuration];
AWSS3PutObjectRequest *logFile = [AWSS3PutObjectRequest new];
logFile.bucket = bucket;
logFile.key = path;
logFile.contentType = [self contentTypeForImageData:self.userPicture];
logFile.body = self.userPicture;
logFile.contentLength = [NSNumber numberWithInteger:[self.userPicture length]];
[[s3 putObject:logFile] continueWithBlock:^id(BFTask *task) {
NSLog(#"Amazon error : %#", [task error]);
return nil;
}];
When using initWithConfiguration:, you must manually retain a strong reference to an instance of AWSS3. One way to accomplish this is to make it a property. Using defaultS3 eliminates the need for this since the AWSS3 class retains the strong reference to the default service client for you.
Hope this helps,