I want the profile picture of users uploaded to my S3 bucket when they register with my app.
Do I need to download the picture first from facebook, or can I pass the external url to the AWSS3TransferManager? PReferable I would of course like to not have to download the picture first to the client's devise and only then upload to S3.
//We create the url to the FB Profile Picture
NSString *compURLString = [NSString stringWithFormat:#"https://graph.facebook.com/FACEBOOKID/picture?type=large"];
NSURL *url = [NSURL URLWithString:compURLString];
// We download the FB Profile Picture from Facebook TBD DO WE NEED THIS STEP OR IS IT POSSIBLE TO PASS THE EXTERNAL URL? AND NOT DOWNLOAD THE IMAGE FIRST? https://graph.facebook.com/FACEBOOKID/picture?type=large TO THE AWS UPLOAD MANAGER
NSURLSessionDownloadTask *downloadPhotoTask = [[NSURLSession sharedSession]
downloadTaskWithURL:url completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
UIImage
// NOw that we have the FB Profile Picture, we upload it to S3
AWSS3TransferManager *uploadManager = [AWSS3TransferManager defaultS3TransferManager];
NSString *const S3BucketName = #"DEMO";
//upload the image
AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.body = location;
uploadRequest.bucket = S3BucketName;
uploadRequest.key = [NSString stringWithFormat:#"#%.jpg", facebookID];
uploadRequest.contentType = #"image/jpg";
[[uploadManager upload:uploadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor]
withBlock:^id(AWSTask *task) {...
}];
[downloadPhotoTask resume];}
Related
I'm trying to download a secure file from S3 server using NSURLSessionDownloadTask,but it returns me 403 error(Access Denied).
My Code:
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"https://xxx.amazonaws.com/bucket-name/file_name"]];
request.HTTPMethod = #"GET";
[request setValue:#"kAccessKey" forHTTPHeaderField:#"accessKey"];
[request setValue:#"kSecretKey" forHTTPHeaderField:#"secretKey"];
NSURLSessionDownloadTask *downloadPicTask = [[NSURLSession sharedSession] downloadTaskWithRequest:request completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
UIImage *downloadedImage = [UIImage imageWithData:
[NSData dataWithContentsOfURL:location]];
dispatch_async(dispatch_get_main_queue(), ^{
weakSelf.imageView.image = downloadedImage;
[weakSelf.activityIndicator stopAnimating];
});
}];
[downloadPicTask resume];
EDIT
I found this code :
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc]initWithRegionType:AWSRegionUSWest2 identityId:#"xxxxxxx" identityPoolId:#"xxxxxxxx" logins:nil];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc]initWithRegion:AWSRegionUSWest2 credentialsProvider:credentialsProvider];
// Construct the NSURL for the download location.
NSString *downloadingFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"sample_img.png"];
NSURL *downloadingFileURL = [NSURL fileURLWithPath:downloadingFilePath];
// Construct the download request.
AWSS3TransferManagerDownloadRequest *downloadRequest = [[AWSS3TransferManagerDownloadRequest alloc]init];
AWSS3TransferManager * transferManager = [AWSS3TransferManager S3TransferManagerForKey:[[configuration credentialsProvider]sessionKey]];
downloadRequest.bucket = #"test-upload-bucket";
downloadRequest.key = #"sample_img.png";
downloadRequest.downloadingFileURL = downloadingFileURL;
[[transferManager download:downloadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor]
withBlock:^id(AWSTask *task){
return nil;
}];
What is the input value for IdentityId and IdentityPoolId ?
A working summer 2017 function, to which you can pass your image name and table cell (I'm downloading logos for certain table entries). Make sure you modify your credentials region, and your key/secret credentials, and your bucket name. Note: Your credentials should not be root. Create a separate IAM user/group/policy and authorize only specific resources (buckets/objects) and specific actions. Create your key and secret. I did this, because I do not want to use amazon's cogito to manage my users. but want my mobile app to access S3 resources directly and securely, and also not via a some redundant server-side script. But, Amazon recommends for mobile, you use cogito and have each user use own/temp creds. Caveat emptor.
-(void) awsImageLoad:(NSString*)imageFile :(UITableViewCell*)cell {
NSArray *filepathelements = [imageFile componentsSeparatedByString:#"/"];
if (filepathelements.count == 0) return;
//extract only the name from a possibe folder/folder/imagename
NSString *imageName = [filepathelements objectAtIndex:filepathelements.count-1];
AWSStaticCredentialsProvider *credentialsProvider =
[[AWSStaticCredentialsProvider alloc]
initWithAccessKey:#"_______________"
secretKey:#"__________________________________"];
//My credentials exist on the US East 1 region server farm
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc]initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
// Construct the NSURL for the temporary download location.
NSString *downloadingFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:imageName];
NSURL *downloadingFileURL = [NSURL fileURLWithPath:downloadingFilePath];
// Construct the download request.
AWSS3TransferManagerDownloadRequest *downloadRequest = [AWSS3TransferManagerDownloadRequest new];
// S3 has only a Global Region -- establish our creds configuration
[AWSS3TransferManager registerS3TransferManagerWithConfiguration:configuration forKey:#"GlobalS3TransferManager"];
AWSS3TransferManager * transferManager = [AWSS3TransferManager S3TransferManagerForKey:#"GlobalS3TransferManager"];
downloadRequest.bucket = #"my_bucket_name";
downloadRequest.key = imageFile;
downloadRequest.downloadingFileURL = downloadingFileURL;
[[transferManager download:downloadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor] withBlock:^id(AWSTask *task){
if (task.error){
if ([task.error.domain isEqualToString:AWSS3TransferManagerErrorDomain]) {
switch (task.error.code) {
case AWSS3TransferManagerErrorCancelled:
case AWSS3TransferManagerErrorPaused:
break;
default:
NSLog(#"Error: %#", task.error);
break;
}
} else {
NSLog(#"Error: %#", task.error);
}
}
if (task.result) {
// ...this runs on main thread already
cell.imageView.image = [UIImage imageWithContentsOfFile:downloadingFilePath];
}
return nil;
}];
}
All HTTP Request need to be properly signed before send to AWS server, the signing Process is very complicated Signature Version 4 Signing Process so I suggest to try AWS Mobile SDK for iOS v2
The example shown by Arun_ is a code snippets that how to use transferManager to download a file via AWS Mobile SDK for iOS v2.
This worked for me:
AWSStaticCredentialsProvider *credentialsProvider = [[AWSStaticCredentialsProvider alloc]initWithAccessKey:#"AccessKey" secretKey:#"secretKey"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc]initWithRegion:AWSRegionUSWest2 credentialsProvider:credentialsProvider];
// Construct the NSURL for the download location.
NSString *downloadingFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:#"sample_img.png"];
NSURL *downloadingFileURL = [NSURL fileURLWithPath:downloadingFilePath];
// Construct the download request.
AWSS3TransferManagerDownloadRequest *downloadRequest = [AWSS3TransferManagerDownloadRequest new];
[AWSS3TransferManager registerS3TransferManagerWithConfiguration:configuration forKey:#"USWest2S3TransferManager"];
AWSS3TransferManager * transferManager = [AWSS3TransferManager S3TransferManagerForKey:#"USWest2S3TransferManager"];
downloadRequest.bucket = #"test-upload-bucket";
downloadRequest.key = #"sample_img.png";
downloadRequest.downloadingFileURL = downloadingFileURL;
[[transferManager download:downloadRequest] continueWithExecutor:[AWSExecutor mainThreadExecutor]
withBlock:^id(AWSTask *task){
return nil;
}];
I have a generic question, experienced developers may laugh at me when they read that but I couldn't manage it somehow.
Basically, I want to upload images from filesystem to my amazon s3 bucket and after that upload links of these images to my server. After I upload images to s3, I got links of them and store them in an array. Then I want to upload those links to my server. But, in order to cover uploading against application suspending I want to make this process run in background. The schema is below.
Upload images to s3 -> get s3 links of these images -> upload links to server
I'm trying to use presignedURL utility of Amazon SDK since Amazon recommends to use it for background uploading.
iOS API docs says NSURLSessionUploadTask does what I'm trying to do. Firstly, I couldn't manage to upload images to s3 with presignedURL (I managed to upload with TransferManagerUploadRequest). Secondly, this process should be running even if application is suspended. But I don't know how to do that actually. I put my "upload-to-s3" code below which is not working right now.
Sorry for the complicated question, it indeed shows my mind right now.
NSString *fileContentTypeStr = #"image/png";
AWSS3GetPreSignedURLRequest *urlRequest = [AWSS3GetPreSignedURLRequest new];
urlRequest.bucket = #"bounty-app";
urlRequest.key =[NSString stringWithFormat:#"submissions/photos/%#",nameOfImage];
urlRequest.HTTPMethod = AWSHTTPMethodPUT;
urlRequest.expires = [NSDate dateWithTimeIntervalSinceNow:3600];
urlRequest.contentType = fileContentTypeStr;
[[[AWSS3PreSignedURLBuilder defaultS3PreSignedURLBuilder] getPreSignedURL:urlRequest]
continueWithBlock:^id(BFTask *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:fileContentTypeStr forHTTPHeaderField:#"Content-Type"];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"unique_name_of_tttask"];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
NSURLSessionUploadTask *uploadTask = [session uploadTaskWithRequest:request fromFile:savedPath];
[uploadTask resume];
}
return nil;
}];
I have an image that I'm uploading to my bucket in AWS this way:
BFTask *task = [BFTask taskWithResult:nil];
[[task continueWithBlock:^id(BFTask *task) {
self.URL = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:#"test"]];
NSData *data = UIImagePNGRepresentation(image);
//NSMutableString *dataString = [NSMutableString new];
[data writeToURL:self.URL atomically:YES];
return nil;
}]continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
self.uploadRequest1 = [AWSS3TransferManagerUploadRequest new];
self.uploadRequest1.bucket = S3BucketName;
self.uploadRequest1.key = S3KeyUploadName1;
self.uploadRequest1.body = self.URL;
return nil;
}];
AWSS3TransferManager *transferManager = [AWSS3TransferManager defaultS3TransferManager];
[[transferManager upload:self.uploadRequest1] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
if (task.error != nil) {
if( task.error.code != AWSS3TransferManagerErrorCancelled
&&
task.error.code != AWSS3TransferManagerErrorPaused
)
{
NSLog(#"Upload Failed!");
}
} else {
self.uploadRequest1 = nil;
NSLog(#"Uploaded!");
}
return nil;
}];
The code for uploading the image works just fine. When I open my bucket I see the image there.
Now what I want to do is to get the URL of that image, is there a way to get the URL without getting the image again?
You don't get it, you create it like:
https://s3.amazonaws.com/BUCKET_NAME/FILE_NAME.jpg
AWSS3TransferManagerUploadRequest *uploadRequest = [AWSS3TransferManagerUploadRequest new];
uploadRequest.body = [NSURL fileURLWithPath:filePath];
uploadRequest.key = fileName;
uploadReuest.bucket = S3BucketName;
[uploadRequest setACL:AWSS3ObjectCannedACLPublicRead];
Above Answer is right but you must have to set "ACL" to uploadRequest.
In your Question,you are forget to set "ACL".
Thanks
I was able to get the path from Amazon S3 -> Bucket -> Folder -> Image file -> Properties and on right side, you will see something like this..
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)?
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;
}];
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:
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.
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.
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.
```
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.
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!
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;
}
}
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");
}
}
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
I'm very much new to objective-c. Basically, all I see is black right now. I'm assigned to code the facebook sharing part of the app we're making, and believe me, I've tried almost all tutorials (none worked, I've tried) and went through the questions here in stack, and I still couldn't upload. So please, someone help?
How to upload videos to facebook via the app? Please provide sample code if you have.
Here is the code I'm trying right now:
-(void)post
{
NSURL *videourl = [NSURL URLWithString:#"https://graph.facebook.com/me/videos"];
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"Here's to the Crazy Ones" ofType:#"mp4"];
NSURL *pathURL = [[NSURL alloc]initFileURLWithPath:filePath isDirectory:NO];
NSData *videoData = [NSData dataWithContentsOfFile:filePath];
NSLog(#"PATH: %#", filePath);
NSDictionary *params = #{
#"title": #"uploading",
#"description": #"testing..."
};
NSLog(#"uploading...");
SLRequest *uploadRequest = [SLRequest requestForServiceType:SLServiceTypeFacebook
requestMethod:SLRequestMethodPOST
URL:videourl
parameters:params];
[uploadRequest addMultipartData:videoData
withName:#"source"
type:#"video/quicktime"
filename:[pathURL absoluteString]];
NSLog(#"Here it goes...");
uploadRequest.account = self.facebookAccount;
NSLog(#"Past uploadrequest...");
[FBSession setActiveSession:FBSession.activeSession];
[uploadRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *responseString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
// [FBSession setActiveSession:FBSession.activeSession];
if (!FBSession.activeSession.isOpen) {
// [FBSession openActiveSessionWithAllowLoginUI: YES];
[FBSession setActiveSession:FBSession.activeSession];
}
NSLog(#"response string: %#", responseString);
NSLog(#"Here I am...");
if(error){
NSLog(#"Error %#", error.localizedDescription);
}else
NSLog(#"%#", responseString);
}];
}
With this I'm getting an error of:
{"error":{"message":"An active access token must be used to query information about the current user.","type":"OAuthException","code":2500}}
Here I provide a link which will guide you step step to upload video to FB Tutorial