AFNetworking get image if it is modified - ios

I am using AFNetworking and download image if it is new image.
After I read though stackoverflow, currently, I am doing like this.
If the image is not modified, there will be cache in http header and I use that fact to check whether image is modified or not.
It is working well for most iOS. But, on iPhone 6s iOS 9.2.1, it always assume as new image.
How shall I detect whether image in server is modified already by using AFNetworking or may be NSUrlConnection?
- (void)downloadSplashScreenFromURL:(NSString *)urlStr
{
BOOL __block responseFromCache = YES; // yes by default
void (^requestSuccessBlock)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject) {
// response was returned from the server, not from cache
NSString *assestName = [urlStr lastPathComponent];
////WRITE TO FILEPATH
NSString *filePath = [splashDirectory() stringByAppendingString:
[NSString stringWithFormat:#"/%#", assestName]];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
DLog(#"Splash : Splash image is empty");
NSData *pngData = UIImagePNGRepresentation(responseObject);
[pngData writeToFile:filePath atomically:YES];
return ;
}
if (responseFromCache) {
// response was returned from cache
DLog(#"SPLASH - RESPONSE FROM CACHE: %#", responseObject);
}
else {
DLog(#"SPLASH - NEW IMAGES FROM SERVER \n Response: %#", responseObject);
NSData *pngData = UIImagePNGRepresentation(responseObject);
[pngData writeToFile:filePath atomically:YES];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:USERDEFAULTS_SPLASH_SCREEN];
[[SplashHelper sharedInstance] showSplash:YES inWindow:[AppDelegate instance].window andSuccessBlock:^{
[[AppDelegate instance] startRunning];
}];
}
};
void (^requestFailureBlock)(AFHTTPRequestOperation *operation, NSError *error) = ^(AFHTTPRequestOperation *operation, NSError *error) {
NSInteger statusCode = operation.response.statusCode;
DLog(#"SPLASH - status code: %lu \nERROR: %#", (long)statusCode, [error localizedDescription]);
DLog(#"SPLASH - ERROR: %#", error);
};
DLog(#"Splash : CALL SPLASH SCREEN HELPER");
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *operation = [manager GET:urlStr
parameters:nil
success:requestSuccessBlock
failure:requestFailureBlock];
[manager.requestSerializer setTimeoutInterval:3.0f];
operation.responseSerializer = [AFImageResponseSerializer serializer];
[operation setCacheResponseBlock:^NSCachedURLResponse *(NSURLConnection *connection, NSCachedURLResponse *cachedResponse) {
// this will be called whenever server returns status code 200, not 304
responseFromCache = NO;
DLog(#"Splash : cachedResponse = %#", cachedResponse);
return cachedResponse;
}];
}

I'm using #import "UIImageView+AFNetworking.h" category in my app to load an image from my server to app. Its working great, whenever an an update made for images on server, it'll generate new URLs, thus when I request with new URLs, AFNetworking will not find a cached image and will load new images from server.
And you should also check this, How do I get cached image stored by AFNetworking's UIImageView category? - there comes requirement when you needs to look after for an image inside your app's cache area.

Related

Upload video chuck downloaded from one server to another server using AFNetworking

I am making an app that will transmit data from glasses to the server for broadcasting.
Til now i am able to download data from glasses to my iPhone document directory.
now i want to upload that downloaded data to my server so that we can broadcast that data to our users.
My iPhone and glasses are connected with each other with the help of WiFi, and i am trying to upload downloaded data via cellular network.
So basically my concept is that download data from glasses and upload that to server.
I have try to make my iPhone as a server to that my back end team could download data from my iPhone.
I got success in this method but the problem is that for this process the client and server should be on same private ip networks.
so now we have left with only one way that we download TS chucks from glasses and same time upload the TS file to our server for broadcasting.
I am using NFNetworking to download video chucks from glasses "TS File" but not able to upload that chucks to my server.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://xxx.xx.xx.x/abc/trunk/WebServices/app/webroot/xyz"]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request] ;
operation.outputStream = [NSOutputStream outputStreamWithURL:[NSURL URLWithString:#"http://abc.aa.a.a/xyz/trunk/WebServices/app/webroot/img/glasses/test/demo.ts"] append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// NSLog(#"Successfully downloaded file to %#", path);
// NSLog(#"download finished!");
if(_delegate && [_delegate respondsToSelector:#selector(ZBTM3U8SegmentDownloaderFinishDownload:)])
{
[_delegate ZBTM3U8SegmentDownloaderFinishDownload:self];
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// NSLog(#"Error: %#", error);
}];
[operation start];
It goes in success state but data is not uploaded on server
server folder show empty.
- (void)postVideoOnServer {
NSDictionary *requestDict = #{ #"uniqueToken":#"101", #"user_id":#"102",};
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:_tsFileName];
NSData *selectedVideo = [NSData dataWithContentsOfFile:path];
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
AFHTTPRequestOperation *operation = [manager POST:#"http://xxx.x.x.xx/myi/public_html/WebServices/broad/axy" parameters:requestDict constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) {
[formData appendPartWithFileData:selectedVideo name:#"file" fileName:#"filename.ts" mimeType:#"video/quicktime"];
} success: ^(AFHTTPRequestOperation *operation, id responseObject)
{
NSInteger statusCode = operation.response.statusCode;
NSLog(#"Status Code ::%d", statusCode);
NSLog(#"Response ::%#", responseObject);
[self handleVideoServiceResponse:responseObject];
}
failure : ^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error: %# %#", operation.responseString, error);
}];
[operation start];
}
it always return request time out error.
Thanks in advance.
Are you sure NSData *selectedVideo is not nil? i doubt you mime type too? Just a suggestion try use MIME: video/MP2T, it should fix your problem.

Memory pressure issue while downloading multiple files using AFNetworking

In my application i am trying to download thousands of images (each image size with a maximum of 3mb) and 10's of videos (each video size with a maximum of 100mb) and saving it in Documents Directory.
To achieve this i am using AFNetworking
Here my problem is i am getting all the data successfully when i am using a slow wifi (around 4mbps), but the same downloading if i am doing under a wifi with a speed of 100mbps the application is getting memory warning while downloading images and memory pressure issue while downloading videos and then application is crashing.
-(void) AddVideoIntoDocument :(NSString *)name :(NSString *)urlAddress{
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlAddress]];
[theRequest setTimeoutInterval:1000.0];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:name];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Successfully downloaded file to %#", path);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
//NSLog(#"Download = %f", (float)totalBytesRead / totalBytesExpectedToRead);
}];
[operation start];
}
-(void)downloadRequestedImage : (NSString *)imageURL :(NSInteger) type :(NSString *)imgName{
NSMutableURLRequest *theRequest=[NSMutableURLRequest requestWithURL:[NSURL URLWithString:imageURL]];
[theRequest setTimeoutInterval:10000.0];
AFHTTPRequestOperation *posterOperation = [[AFHTTPRequestOperation alloc] initWithRequest:theRequest];
posterOperation.responseSerializer = [AFImageResponseSerializer serializer];
[posterOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
//NSLog(#"Response: %#", responseObject);
UIImage *secImg = responseObject;
if(type == 1) { // Delete the image from DB
[self removeImage:imgName];
}
[self AddImageIntoDocument:secImg :imgName];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Image request failed with error: %#", error);
}];
[posterOperation start];
}
The above code i am looping according to the number of videos and images that i have to download
What is the reason behind that behaviour
I even have screen shots of memory allocation for both the scenarios
Please Help
Adding code for saving the downloaded images also
-(void)AddImageIntoDocument :(UIImage *)img :(NSString *)str{
if(img) {
NSData *pngData = UIImageJPEGRepresentation(img, 0.4);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *filePathName =[[paths objectAtIndex:0]stringByAppendingPathComponent:str];
[pngData writeToFile:filePathName atomically:YES];
}
else {
NSLog(#"Network Error while downloading the image!!! Please try again.");
}
}
The reason for this behavior is that you're loading your large files into memory (and presumably it's happening quickly enough that you app isn't having a chance to respond to memory pressure notifications).
You can mitigate this by controlling the peak memory usage by not loading these downloads into memory. When download large files, it's often better to stream them directly to persistent storage. To do this with AFNetworking, you can set the outputStream of the AFURLConnectionOperation, and it should stream the contents directly to that file, e.g.
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSString *documentsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *path = [documentsPath stringByAppendingPathComponent:[url lastPathComponent]]; // use whatever path is appropriate for your app
operation.outputStream = [[NSOutputStream alloc] initToFileAtPath:path append:NO];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"successful");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"failure: %#", error);
}];
[self.downloadQueue addOperation:operation];
BTW, you'll notice that I'm not just calling start on these requests. Personally, I always add them to a queue for which I've specified the maximum number of concurrent operations:
self.downloadQueue = [[NSOperationQueue alloc] init];
self.downloadQueue.maxConcurrentOperationCount = 4;
self.downloadQueue.name = #"com.domain.app.downloadQueue";
I think this is less critical regarding memory usage than the streaming of the results directly to a outputStream using persistent storage, but I find this is another mechanism for managing system resources when initiating many concurrent requests.
You can start using NSURLSession's downloadTask.
I think this will resolve your issue.
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://someSite.com/somefile.zip"]];
[[NSURLSession sharedSession] downloadTaskWithRequest:request
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
{
// Use location (it's file URL in your system)
}];

Memory pressure due to download and saving of images

Fortunately I know where my memory pressure issue is coming from, and I have tried a number of techniques such as wrapping a block in an #autorelease block and setting objects to nil but still no success.
Sorry for dumping too much code here, I tried to cut it down to the essentials. Here is the code for downloading and saving images:
NSMuttableArray *photosDownOps = [NSMuttableArray array];
NSURL *URL = [...];
NSURLRequest *request = [...];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFImageResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
dispatch_queue_t amBgSyncQueue = dispatch_queue_create("writetoFileThread", NULL);
dispatch_async(amBgSyncQueue, ^{
[self savePhotoToFile:(UIImage *)responseObject usingFileName:photo.id];
});
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if ([error code] != NSURLErrorCancelled)
NSLog(#"Error occured downloading photos: %#", error);
}];
[photosDownOps addObject:op];
NSArray *photosDownloadOperations = [AFURLConnectionOperation batchOfRequestOperations:photosDownloadOperatons
progressBlock:^(NSUInteger nof, NSUInteger tno) {
} completionBlock:^(NSArray *operations) {
NSLog(#"all photo downloads completed");
}];
[self.photosDownloadQueue addOperations:photosDownloadOperations waitUntilFinished:NO];
+ (void) savePhotoToFile:(UIImage *)imageToSave usingFileName:(NSNumber *)photoID{
#autoreleasepool {
NSData * binaryImageData = UIImageJPEGRepresentation(imageToSave, 0.6);
NSString *filePath = [Utilities fullPathForPhoto:photoID];
[binaryImageData writeToFile:filePath atomically:YES];
binaryImageData = nil;
imageToSave = nil;
}
}
This situation though only happens with iPhone 4s devices that I have tested on, it does not happen on iPhone 5 models.
I managed to solve this by extending NSOperation and within the main block immediately after I receive the data I write it out to file:
- (void)main{
#autoreleasepool {
//...
NSData *imageData = [[NSData alloc] initWithContentsOfURL:imageUrl];
if (imageData) {
NSError *error = nil;
[imageData writeToFile:imageSavePath options:NSDataWritingAtomic error:&error];
}
//...
}
}
This NSOperation object was then added a NSOperationQueue I already had.
Try to create your own class to download image using NSUrlConnection and in the delegate method append that data to your file just see the below code
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
NSFileHandle *fileHandle = [NSFileHandle fileHandleForWritingAtPath:aPath];
[fileHandle seekToEndOfFile];
[fileHandle writeData:data];
[fileHandle closeFile];
}
This will help you in memory management as all the data which is download is not need to cache .

AFNetworking not uploading image

I am trying to upload an image via AFnetworking. I am able to get the image url, and it does contact my server. However, it won't upload. The file upload folder is empty and when I get back my JSON response, it is "null"
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
UIImage *image = [info valueForKey:UIImagePickerControllerOriginalImage];
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// Request to save the image to camera roll
[library writeImageToSavedPhotosAlbum:[image CGImage] orientation:(ALAssetOrientation)[image imageOrientation] completionBlock:^(NSURL *assetURL, NSError *error){
if (error) {
NSLog(#"error");
} else {
NSLog(#"url %#", assetURL);
NSData *data = [NSData dataWithContentsOfURL:assetURL];
NSString *path = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
path = [path stringByAppendingString:#"/image.jpg"];
[data writeToFile:path atomically:YES];
[self uploadPhoto:path];
// NSLog(path);
[self dismissModalViewControllerAnimated:NO];
}
}];
}
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *parameters = #{#"foo": self.targetid};
NSURL *filePath = [NSURL fileURLWithPath:file];
[manager POST:#"http:/****/uploadpics.php" parameters:parameters constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:filePath name:#"image" error:nil];
} success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
image url looks like this:
/var/mobile/Applications/FFCAE923-1115-4209-AB39-D9D1ACEB9CB7/Documents/yourLocalImage.png
I can't seem to figure out what am I doing wrong.. The script is fine because it works for android just as it is supposed to...
PHP:
$name = $_FILES['filename']['name'];
if (is_uploaded_file($_FILES['filename']['tmp_name'])){
if (move_uploaded_file($_FILES['filename']['tmp_name'], $folder.$_FILES ['filename'] ['name'])) {
Echo $foname;
} else {
}
} else {
}
Your upload code names the file image but your script seems to expect filename. I haven't done any php for a while but I think they should match.
There is another method which allows you to specify more details about the part that you're appending to the form data so you probably need that to set the appropriate names.

Uploading images to server

I am trying to upload an image to a server (that is already built) and I am getting errors like Request has timed out. Other methods of sending text and fetch data from the server are working properly. However, sending an image I found it hard to do it.
I am using the following code at the moment:
-(void)uploadImage:(NSData*)image callbackBlock: (void (^)(BOOL success)) callbackBlock
{
NSString *path = [NSString stringWithFormat:#"upload"];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:image, #"image", nil];
[params addEntriesFromDictionary:self.sessionManager.authParameters];
NSMutableURLRequest *request = [self multipartFormRequestWithMethod:#"POST" path:path parameters:params constructingBodyWithBlock: ^(id <AFMultipartFormData>formData){
[formData appendPartWithFormData:image name:#"Image"];
}];
AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"!!!Response object: %#",responseObject);
callbackBlock(YES);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#",error.description);
callbackBlock(NO);
}];
[self enqueueHTTPRequestOperation:operation];
}
Do you have any idea what the problem is? Can you give me some suggestions or possible errors on the above code.
Thank you very much.
You can send your image as a base64 encoded text... This should work.
You can use this category to create base64 encoded image:
https://github.com/l4u/NSData-Base64

Resources