Amazon s3 transfer utility not working in background - ios

i have to upload a large file like more then 4gb onto amazon s3. amazon sdk giving two options for uploading and downloading from s3. one is awss3transfermanager and other is awss3transferutility. i actually want to use awss3transferutility because i want the app to continue uploading in background. awss3transferutility has two functions uploadFile and other uploadFileUsingMultiPart . uploadFile function actually works in background but uploading starts from 0 on network changes or drop. For that reason i am curretly using uploadFileUsingMultiPart function so the uploading won't get restart from 0 on networks fail. but this uploadFileUsingMultiPart function won't continue uploading in background. In their latest release they introduce this uploadFileUsingMultiPart function inside awstransferutility. so i was expecting that uploading will continue in background with network fail but it won't continue in background. i just wanted to ask is it sdk related bug or i am doing something wrong
this is the code i am curretly
//in app delegate
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler {
[AWSS3TransferUtility interceptApplication:application
handleEventsForBackgroundURLSession:identifier
completionHandler:completionHandler];
}
// in ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
__weak typeof(self) weakSelf = self;
expression = [AWSS3TransferUtilityMultiPartUploadExpression new];
expression.progressBlock = ^(AWSS3TransferUtilityMultiPartUploadTask * task, NSProgress * progress) {
typeof(self) newWeakSelf = weakSelf;
dispatch_async(dispatch_get_main_queue(), ^{
// Do something e.g. Alert a user for transfer completion.
NSLog(#"progress value %f",progress.fractionCompleted);
// On failed uploads, `error` contains the error object.
newWeakSelf->progressView.progress = progress.fractionCompleted;
});
};
completionHandler = ^(AWSS3TransferUtilityMultiPartUploadTask *task, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"uploading completed ");
});
};
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#"us-west-1:7a24b199-e4b2-4657-9627-sdfs4ssdff"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];
// AWSServiceManager.defaultServiceManager.defaultServiceConfiguration = configuration;
AWSS3TransferUtilityConfiguration *tfConfig = [AWSS3TransferUtilityConfiguration new];
tfConfig.retryLimit = 5;
tfConfig.multiPartConcurrencyLimit = [NSNumber numberWithInteger:3];
[AWSS3TransferUtility registerS3TransferUtilityWithConfiguration:configuration transferUtilityConfiguration:tfConfig forKey:#"transfer-utility-with-advanced-options"];
transferUtility = [AWSS3TransferUtility S3TransferUtilityForKey:#"transfer-utility-with-advanced-options"];
}
-(void)startUploading {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"test" ofType:#"mp4"];
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
NSString *fileContentTypeStr = #"video/mp4";
// NSData *data = [NSData dataWithContentsOfURL:fileURL];
// AWSTask *task = [transferUtility uploadDataUsingMultiPart:data bucket:#"sibme-development" key:#"temp/testfilenew/testfile1.mp4" contentType:fileContentTypeStr expression:expression completionHandler:completionHandler ];
AWSTask *task = [transferUtility uploadFileUsingMultiPart:fileURL bucket:#"development" key:#"temp/testfilenew/testfile.mp4" contentType:fileContentTypeStr expression:expression completionHandler:completionHandler];
[task continueWithBlock:^id _Nullable(AWSTask * _Nonnull t) {
if (t.result) {
self->uplaodTask = t.result;
}
return nil;
}];
}

I am not expert on this, but I noticed something in your code - Bucket region:
initWithRegionType:AWSRegionUSEast1
identityPoolId:#"us-west-1:...
Once I had similar mismatch in my code, the bucket region and the Cognito identity pool region did not match, and that was causing problems. This may or may not be the case in your code, but just adding some info based on my experience.
AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:AWSRegionUSEast1 identityPoolId:#"us-west-1:7a24b199-e4b2-4657-9627-sdfs4ssdff"];
AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:AWSRegionUSEast1 credentialsProvider:credentialsProvider];

If you think there is a bug with the SDK, you can try with the latest version of the SDK and if you are still seeing issues, open an Issue over in https://github.com/aws-amplify/aws-sdk-ios with some repro steps.

Related

How to upload Image AWSS3 TransferManager upload Request objective c?

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

S3TransferManager not able to save file/data on S3GetObjectRequest targetedFilePath in ios

I am an iOS developer using Amazon S3 to upload and download the data. I successfully uploaded many image files, but when I am trying to download those all files, I am not able to save data or file on targeted path of S3GetObjectRequest. I am requesting object through the S3TransferManager.
Below is my code
- (void)listObjects:(id)sender {
self.collection = [[NSMutableArray alloc] init];
s3Client = [[AmazonS3Client alloc] initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY];
#try {
S3ListObjectsRequest *req = [[S3ListObjectsRequest alloc] initWithName:AMAZON_BUCKET_NAME];
req.prefix = #"arvinds/8/";
//req.prefix = [NSString stringWithFormat:#"%#", self.appUser.userEmail];
S3ListObjectsResponse *resp = [s3Client listObjects:req];
NSMutableArray* objectSummaries = resp.listObjectsResult.objectSummaries;
for (int x = 0; x < [objectSummaries count]; x++) {
NSLog(#"objectSummaries: %#",[objectSummaries objectAtIndex:x]);
S3ObjectSummary *s3Object = [objectSummaries objectAtIndex:x];
NSString *downloadingFilePath = [[NSTemporaryDirectory() stringByAppendingPathComponent:#"download"] stringByAppendingPathComponent:s3Object.key];
NSURL *downloadingFileURL = [NSURL fileURLWithPath:downloadingFilePath];
NSLog(#"downloadingFilePath--- %#",downloadingFilePath);
if ([[NSFileManager defaultManager] fileExistsAtPath:downloadingFilePath]) {
[self.collection addObject:downloadingFileURL];
} else {
[self.collection addObject:downloadingFileURL];
S3GetObjectRequest *getObj = [[S3GetObjectRequest new] initWithKey:s3Object.key withBucket:AMAZON_BUCKET_NAME];
getObj.targetFilePath = downloadingFilePath;
getObj.delegate = self;
[self download:getObj];// Start downloding image and write in default folder
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.GalleryCollectionView reloadData];
//[self downloadAllRequestObject];
});
}
#catch (NSException *exception) {
NSLog(#"Cannot list S3 %#",exception);
}
}
// Above code is for access all the objects stored on Amazon S3 server.
// Below code is for S3TransferManager request
- (void)download:(S3GetObjectRequest *)downloadRequest {
S3TransferManager *transferManager = [S3TransferManager new];
transferManager.s3 = s3Client;
transferManager.delegate = self;
[transferManager download:downloadRequest];
}
When executing this, it is not saving data on the target path.
- download: is an asynchronous method and returns immediately. Since you are not retaining a strong reference to an instance of S3TransferManager, it can be released before the download completes. You need to keep a strong reference to transferManager.
Please note that you are using a deprecated version of the AWS SDK. It may be worthwhile to migrate to the version 2 of the AWS Mobile SDK for iOS. With the latest version of the SDK, you do not need to worry about the memory retention issue you are encountering.

AWS S3 SDK v2 for iOS - Download an image file to UIImage

Seems this should be relatively simple. I'm using the AWS SDK (v2) for iOS and I'm trying to download a .png file and display it to the screen in a UIImage. Everything actually works! Just very strangely...
Here is my code:
AWSStaticCredentialsProvider *credentialsProvider = [AWSStaticCredentialsProvider credentialsWithAccessKey:#"MY_ACCESS_KEY" secretKey:#"MY_SECRET_KEY"];
AWSServiceConfiguration *configuration = [AWSServiceConfiguration configurationWithRegion:AWSRegionUSWest1 credentialsProvider:credentialsProvider];
[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
AWSS3 *transferManager = [[AWSS3 alloc] initWithConfiguration:configuration];
AWSS3GetObjectRequest *getImageRequest = [AWSS3GetObjectRequest new];
getImageRequest.bucket = #"MY_BUCKET";
getImageRequest.key = #"MY_KEY";
[[transferManager getObject:getImageRequest] continueWithBlock:^id(BFTask *task) {
if(task.error)
{
NSLog(#"Error: %#",task.error);
}
else
{
NSLog(#"Got image");
NSData *data = [task.result body];
UIImage *image = [UIImage imageWithData:data];
myImageView.image = image;
}
return nil;
}];
When this code gets executed, the continueWithBlock gets executed, there is no task error, so Got image is logged. And this happens fairly quickly. However, it's not until about 10 seconds later that the UIImageView updates on the screen. I even ran through the debugger to see if any of the lines following the NSLog(#"Got image"); line were taking long and they weren't. They were all executing very quickly but then the UIImageView would not be updated on the UI.
The issue is that you are updating UI component from a background thread. The continueWithBlock: block is executed in the background thread, and it is causing the aforementioned behavior. You have two options:
Use Grand Central Dispatch in the block and run it on the main thread:
...
NSURL *fileURL = [task.result body];
NSData *data = // convert fileURL to data
dispatch_async(dispatch_get_main_queue(), ^{
UIImage *image = [UIImage imageWithData:data];
myImageView.image = image;
});
...
Use mainThreadExecutor to run the block on the main thread:
[[transferManager getObject:getImageRequest] continueWithExecutor:[BFExecutor mainThreadExecutor]
withBlock:^id(BFTask *task) {
...
Hope this helps,

Downloading data and processing asynchronous with NSURLSession

I am using NSURLSession to download xml files and then I want to do different processing to this files, like parsing them:
-(void)parseFeed:(NSURL *)url
{
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
NSURLSessionDataTask* task = [FeedSessionManager.sharedManager.session dataTaskWithRequest:request completionHandler:^(NSData* data, NSURLResponse* response, NSError* error)
{
Parser* parser = [[Parser alloc] initWithData:data];
[self.feeds addObjectsFromArray:[parser items]];
}];
[task resume];
}
Parser object will parse the xml file using NSXMLParser. The parseFeed:(NSURL*)url is called from the ViewController:
Downloader* downloader = [[Downloader alloc] init];
[downloader parseFeed:[NSURL URLWithString:#"http://www.engadget.com/tag/features/rss.xml"]];
NSArray* items = [downloader feeds];
And this is how I create the NSURLSession object:
-(id)init
{
if(self = [super init])
{
_session = [NSURLSession sessionWithConfiguration:FeedSessionConfiguration.defaultSessionConfiguration delegate:self delegateQueue:nil];
}
return self;
}
Of course this approach doesn't work for me. Inside parseFeed method I want to wait until all data is downloaded and processed. Only then I want to access the self.feeds array in the ViewController.
Can someone point me into the right direction into doing this ? Or maybe point me to a different approach ?
I have used ASIHTTPRequest but now no longer maintained but you can use AFHTTPClient's operation queue
AFHTTPClient *client = [[AFHTTPClient alloc] initWithBaseURL:nil];
// Important if only downloading one file at a time
[client.operationQueue setMaxConcurrentOperationCount: 1];
NSArray *videoURLs; // An array of strings you want to download
for (NSString * videoURL in videoURLs) {
// …setup your requests as before
[client enqueueHTTPRequestOperation:downloadRequest];
}

Using AVURLAsset on a custom NSURLProtocol

I have written a custom NSURLProtocol (called "memory:") that allows me to fetch stored NSData items from a NSDictionary based on a name. For example, this code registers the NSURLProtocol class and adds some data:
[VPMemoryURLProtocol register];
[VPMemoryURLProtocol addData:data withName:#"video"];
This allows me to refer to the NSData via a url like "memory://video".
Below is my custom NSURLProtocol implementation:
NSMutableDictionary* gMemoryMap = nil;
#implementation VPMemoryURLProtocol
{
}
+ (void)register
{
static BOOL inited = NO;
if (!inited)
{
[NSURLProtocol registerClass:[VPMemoryURLProtocol class]];
inited = YES;
}
}
+ (void)addData:(NSData *)data withName:(NSString *)name
{
if (!gMemoryMap)
{
gMemoryMap = [NSMutableDictionary new];
}
gMemoryMap[name] = data;
}
+ (BOOL)canInitWithRequest:(NSURLRequest *)request
{
NSLog(#"URL: %#, Scheme: %#",
[[request URL] absoluteString],
[[request URL] scheme]);
NSString* theScheme = [[request URL] scheme];
return [theScheme caseInsensitiveCompare:#"memory"] == NSOrderedSame;
}
+ (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request
{
return request;
}
- (void)startLoading
{
NSString* name = [[self.request URL] path];
NSData* data = gMemoryMap[name];
NSURLResponse* response = [[NSURLResponse alloc] initWithURL:[self.request URL]
MIMEType:#"video/mp4"
expectedContentLength:-1
textEncodingName:nil];
id<NSURLProtocolClient> client = [self client];
[client URLProtocol:self didReceiveResponse:response
cacheStoragePolicy:NSURLCacheStorageNotAllowed];
[client URLProtocol:self didLoadData:data];
[client URLProtocolDidFinishLoading:self];
}
- (void)stopLoading
{
}
I am not sure whether this code works or not but that is not what I have a problem with. Despite registering the custom protocol, canInitWithRequest: is never called when I try to use the URL in this code:
NSURL* url = [NSURL URLWithString:#"memory://video"];
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil];
AVAssetImageGenerator* imageGen = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
CMTime time = CMTimeMakeWithSeconds(0, 600);
NSError* error;
CMTime actualTime;
CGImageRef image = [imageGen copyCGImageAtTime:time
actualTime:&actualTime
error:&error];
UIImage* uiImage = [UIImage imageWithCGImage:image];
CGImageRelease(image);
image is always nil if I use "memory://video" but works fine if I use "file:///...". What am I missing? Why isn't canInitWithRequest not being called? Does AVFoundation only support specific URL protocols and not custom ones?
Thanks
Certainly the underpinnings used to only support particular URL schemes— as an eBook developer I've seen this happen for any media type loaded through a URL such as epub:// or zip://. In those cases, on iOS 5.x and earlier, tracing through the relevant code would wind up in a QuickTime method which compared the URL scheme against a small number of supported ones: file, http, https, ftp and whatever it is that iTunes uses-- I forget what it's called.
In iOS 6+ there is a new API in AVFoundation, however, which is designed to help here. While I've not used it personally, this is how it should work:
NSURL* url = [NSURL URLWithString:#"memory://video"];
AVURLAsset* asset = [[AVURLAsset alloc] initWithURL:url options:nil];
////////////////////////////////////////////////////////////////
// NEW CODE START
AVAssetResourceLoader* loader = [asset resourceLoader];
id<AVAssetResourceLoaderDelegate> delegate = [SomeClass newInstanceWithNSURLProtocolClass: [VPMemoryURLProtocol class]];
[loader setDelegate: delegate queue: some_dispatch_queue];
// NEW CODE END
////////////////////////////////////////////////////////////////
AVAssetImageGenerator* imageGen = [AVAssetImageGenerator assetImageGeneratorWithAsset:asset];
CMTime time = CMTimeMakeWithSeconds(0, 600);
With this in place, you need only implement the AVAssetResourceLoader protocol somewhere, which is very simple as it contains only one method. Since you already have an NSURLProtocol implementation, all your real work is done and you can simply hand off the real work to the Cocoa loading system or your protocol class directly.
Again, I'll point out that I've yet to actually make use of this, so the above is entirely theoretical.

Resources