Uploading image from gallery with AFNetworking 3.0 - ios

I checked the code example to upload images via multipart.
But I don't know how get the #"file://path/to/image.jpg" value.
This is what I tried:
-(void) uploadAvatar:(NSString*)file
success:(void (^)())success failure:(void (^)())failure {
NSString* url = [NSString stringWithFormat:#"%#%#%#", res, kApiRest, kApiServiceAvatar];
//NSString* filepath = [NSString stringWithFormat:#"%#%#", res, kApiRest];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:file] name:#"file" fileName:#"image.jpg" mimeType:#"image/*" error:nil];
} error:nil];
//AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [self.manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
// [progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#", error);
failure();
} else {
NSLog(#"%# %#", response, responseObject);
success();
}
}];
[uploadTask resume];
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[self dismissViewControllerAnimated:YES completion:nil];
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
UIImage *imageToUse;
// Imagen
if (CFStringCompare ((CFStringRef) mediaType, kUTTypeImage, 0) == kCFCompareEqualTo) {
imageToUse = (UIImage *) [info objectForKey: UIImagePickerControllerEditedImage];
imageToUse = [imageToUse scaleToMaxWidth:kAvatarSize];
NSData* fileHD = UIImagePNGRepresentation(imageToUse);
APIAvatar* api = [[APIAvatar alloc] initWithUser:_user.jid password:_user.pass];
//I have the NSData object fileHD, how can I get the path?
[api uploadAvatar:[fileHD ] success:<#^(void)success#> failure:<#^(void)failure#>]
}
}

Try this solution
-(void) uploadAvatar:(NSData*)fileData
success:(void (^)())success failure:(void (^)())failure {
NSString* url = [NSString stringWithFormat:#"%#%#%#", res, kApiRest, kApiServiceAvatar];
//NSString* filepath = [NSString stringWithFormat:#"%#%#", res, kApiRest];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:url parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData: fileData
name:#"file"
fileName:#"image.jpg" mimeType:#"image/*"];
} error:nil];
//AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [self.manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
// [progressView setProgress:uploadProgress.fractionCompleted];
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#", error);
failure();
} else {
NSLog(#"%# %#", response, responseObject);
success();
}
}];
[uploadTask resume];
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
[self dismissViewControllerAnimated:YES completion:nil];
NSString *mediaType = [info objectForKey: UIImagePickerControllerMediaType];
UIImage *imageToUse;
// Imagen
if (CFStringCompare ((CFStringRef) mediaType, kUTTypeImage, 0) == kCFCompareEqualTo) {
imageToUse = (UIImage *) [info objectForKey: UIImagePickerControllerEditedImage];
imageToUse = [imageToUse scaleToMaxWidth:kAvatarSize];
NSData* fileHD = UIImagePNGRepresentation(imageToUse);
APIAvatar* api = [[APIAvatar alloc] initWithUser:_user.jid password:_user.pass];
//I have the NSData object fileHD, how can I get the path?
[api uploadAvatar: fileHD success:<#^(void)success#> failure:<#^(void)failure#>]
}
}
Update
If you want set headers, manager's headers are ignored.
Instead they should be set on request object.
[request setValue:#"myheader1" forHTTPHeaderField:#"key1"];
[request setValue:#"myheader2" forHTTPHeaderField:#"key2"];

Related

AFNetworking Upload multiple files in sequence

I want to upload files sequentially. To be uploaded to the server one, then the second and so on.
When I run the following code, all the files seem to be loaded at once. In addition, I can not track the progress in MBHudProgress.
Can I somehow make it so that I get a response from the file loader that the previous file is loaded and can I load the next one?
for (id item in imagesArray) {
NSLog(#"item %#", item);
NSData *imageData = UIImagePNGRepresentation(item);
NSString *urlUpload = #"https://domain/api/wp-json/wp/v2/media?access_token=";
urlUpload = [urlUpload stringByAppendingString:[Lockbox unarchiveObjectForKey:#"access_token"]];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.label.text = #"Uploaded photo";
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:urlUpload parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData name:#"file" fileName:#"filename.png" mimeType:#"image/png"];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
hud.progress = uploadProgress.fractionCompleted;
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[hud hideAnimated:YES];
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
if ([responseObject objectForKey:#"id"] == nil ||
[[responseObject objectForKey:#"id"] isEqual:[NSNull null]] ||
[responseObject objectForKey:#"id"] == [NSNull null]) {
NSLog(#"NO ID %#", responseObject);
} else {
NSLog(#"ID: %#", [responseObject objectForKey:#"id"]);
NSLog(#"source_url: %#", [responseObject objectForKey:#"source_url"]);
}
}
}];
[uploadTask resume];
}
As I understand, you want to upload image one by one. After first image is uploaded, start upload next image.
In my opinion, you can use recursive in this case. Take a look at my code below.
// Use recursive to upload an array items
- (void)startUploadItems:(NSMutableArray*)items {
if (items.count < 1) {
return;
}
[self startUploadItem:items[0] completion:^(BOOL success) {
[items removeObjectAtIndex:0];
[self startUploadItems:items];
}];
}
// Use to upload a single item.
- (void)startUploadItem:(id)item completion:(void(^)(BOOL success))completion {
NSLog(#"item %#", item);
NSData *imageData = UIImagePNGRepresentation(item);
NSString *urlUpload = #"https://domain/api/wp-json/wp/v2/media?access_token=";
urlUpload = [urlUpload stringByAppendingString:[Lockbox unarchiveObjectForKey:#"access_token"]];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeAnnularDeterminate;
hud.label.text = #"Uploaded photo";
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:urlUpload parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:imageData name:#"file" fileName:#"filename.png" mimeType:#"image/png"];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
hud.progress = uploadProgress.fractionCompleted;
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[hud hideAnimated:YES];
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
if ([responseObject objectForKey:#"id"] == nil ||
[[responseObject objectForKey:#"id"] isEqual:[NSNull null]] ||
[responseObject objectForKey:#"id"] == [NSNull null]) {
NSLog(#"NO ID %#", responseObject);
} else {
NSLog(#"ID: %#", [responseObject objectForKey:#"id"]);
NSLog(#"source_url: %#", [responseObject objectForKey:#"source_url"]);
}
}
if (completion) {
completion(!error);
}
}];
[uploadTask resume];
}
Usage:
[self startUploadItems:imagesArray];

Unable to save picture in SignUp POST request with Afnetworking

I am trying to upload a picture on server with Multiparts using AFNetworking. I have tried to make a simple POST request without Image and it works fine. Now, can say that service URL is absolutely fine and there is no issue with server and I can see that image URL that is saved in document directory is also fine and all the other parameters are fine too, because they all are working with simple request. Can anyone find some error in my code? My Code is:
(void)uploadPicture:(NSMutableDictionary *)param
{
NSString *string=[NSString stringWithFormat:#"%#%#",base_url,#"register"];
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:string parameters:param constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:getImagePath] name:#"picture" fileName:getImagePath mimeType:#"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
AFJSONRequestSerializer *serializer = [AFJSONRequestSerializer serializer];
[serializer setStringEncoding:NSUTF8StringEncoding];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
// This is not called back on the main queue.
// You are responsible for dispatching to the main queue for UI updates
dispatch_async(dispatch_get_main_queue(), ^{
//Update the progress view
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
NSDictionary *dict= [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
NSDictionary *dic=responseObject;
NSLog(#"");
}
}];
[uploadTask resume];
}
Try this function :
First Solution
Note : You need to customise as per your need
-(void)getResponeseWithURL:(NSString *)url WithParameter:(NSDictionary *)parameter WithImage:(UIImage *)image ImageArray:(NSMutableArray *)arrImage WithImageParameterName:(NSString *)imagename WithCallback:(void(^)(BOOL success, id responseObject))callback {
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:[NSString stringWithFormat:#"%#%#",BASEURL,url] parameters:parameter constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
if (image) {
[formData appendPartWithFileData:UIImageJPEGRepresentation(image, 0.8) name:imagename fileName:#"Image.jpg" mimeType:#"image/jpeg"];
}
else if (arrImage){
int i = 1;
for (UIImage *recipeimage in arrImage) {
// this condition for maintain server side coloum format : ex name , name_2 , name_3
[formData appendPartWithFileData:UIImageJPEGRepresentation(recipeimage, 0.8) name:i == 1 ? imagename : [NSString stringWithFormat:#"%#_%d",imagename,i] fileName:#"Image.jpg" mimeType:#"image/jpeg"];
i++;
}
}
}error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
manager.responseSerializer = [AFJSONResponseSerializer serializer];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager
uploadTaskWithStreamedRequest:request
progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
});
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
[UtilityClass showAlertWithMessage: #"Please try again" andWithTitle:#"Network Error" WithAlertStyle:AFAlertStyleFailure];
NSLog(#"Error: %#", [[NSString alloc]initWithData:[[error valueForKey:#"userInfo"] valueForKey:#"com.alamofire.serialization.response.error.data"] encoding:NSUTF8StringEncoding]);
[UtilityClass removeActivityIndicator];
callback(NO,nil);
} else {
callback(YES,responseObject);
}
}];
[uploadTask resume];
}
Second
May you have forgot to add ATS in your project plist file so you need to add this .
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
NSString *string=[NSString stringWithFormat:#"%#%#",base_url,#"register"];
#autoreleasepool {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager POST:strurl
parameters:parameters
constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSMutableArray *allKeys = [[imgParameters allKeys] mutableCopy];
for (NSString *key in allKeys) {
id object = [imgParameters objectForKey:key];
int timestamp = [[NSDate date] timeIntervalSince1970];
NSString *str = [[NSString alloc] initWithFormat:#"%d", timestamp];
NSString *ranstrin = [self randomStringWithLength:8];
// if ([key isEqualToString:#"image"]) {
str = [NSString stringWithFormat:#"TestThumb_%d_%#.jpg",
timestamp, ranstrin];
[formData appendPartWithFileData:object
name:key
fileName:str
mimeType:#"image/jpeg"];
}
}
progress:^(NSProgress *_Nonnull uploadProgress) {
}
success:^(NSURLSessionDataTask *_Nonnull task,
id _Nullable responseObject) {
complete(responseObject, nil);
}
failure:^(NSURLSessionDataTask *_Nullable task,
NSError *_Nonnull error) {
complete(nil, error);
}];

AFNetworking 3.0 upload image

Im trying to upload image to my server by using AFNetworking 3.0. My server returns "Please select a file to upload.". Here is how i catch the uploaded file in my php file $filename = $_FILES["file"]["name"];.
-(void)uplodeImages :(NSString *)image {
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:#"http://local/upload.php" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileURL:[NSURL fileURLWithPath:image] name:#"file" fileName:#"imagename.jpg" mimeType:#"image/jpeg" error:nil];
} error:nil];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Laddar...");
});
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
if (error) {
NSLog(#"Error: %#", error);
} else {
NSLog(#"%# %#", response, responseObject);
}
}];
[uploadTask resume];
}
Note :- I have just implemented Image Upload service using AFNetworking 3.0,
-> Here kBaseURL means Base URL of server
->serviceName means Name of Service.
->dictParams means give parameters if needed, otherwise nil.
->image means pass your image.
Note :- This code written in NSObject Class and we have apply in our Project.
+ (void)requestPostUrlWithImage: (NSString *)serviceName parameters:(NSDictionary *)dictParams image:(UIImage *)image success:(void (^)(NSDictionary *responce))success failure:(void (^)(NSError *error))failure {
NSString *strService = [NSString stringWithFormat:#"%#%#",kBaseURL,serviceName];
[SVProgressHUD show];
NSData *fileData = image?UIImageJPEGRepresentation(image, 0.5):nil;
NSError *error;
NSMutableURLRequest *request = [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:strService parameters:dictParams constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
if(fileData){
[formData appendPartWithFileData:fileData
name:#"image"
fileName:#"img.jpeg"
mimeType:#"multipart/form-data"];
}
} error:&error];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
NSURLSessionUploadTask *uploadTask;
uploadTask = [manager uploadTaskWithStreamedRequest:request progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(#"Wrote %f", uploadProgress.fractionCompleted);
}
completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[SVProgressHUD dismiss];
if (error)
{
failure(error);
}
else
{
NSLog(#"POST Response : %#",responseObject);
success(responseObject);
}
}];
[uploadTask resume];
}
--> Now apply in our project.
UIImage *imgProfile = //Pass your image.
[WebService requestPostUrlWithImage:#"save-family-member" parameters:nil image:imgProfile success:^(NSDictionary *responce) {
NSString *check = [NSString stringWithFormat:#"%#",[responce objectForKey:#"status"]];
if ([check isEqualToString: #"1"]) {
//Image Uploaded.
}
else
{
//Failed to Upload.
}
} failure:^(NSError *error) {
//Error
}];

AFNetworking 3.0 Background Upload not working

I have implemented
-(void)uploadMultipleImagesUsingAFNetworkingMultipartFormat:(id)sender {
NSArray * directoryContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[[FileUtility basePath] stringByAppendingPathComponent:[self getPathOfRootImageFolderDocumentId:#"1"]] error:nil];
NSString *path = [directoryContents objectAtIndex:0];
NSString* filePath=[[[FileUtility basePath] stringByAppendingPathComponent:[self getPathOfRootImageFolderDocumentId:#"1"]] stringByAppendingPathComponent:path];
NSURLSessionConfiguration *config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:#"com.uploadDocument.Background"];
MBProgressHUD *progres = [MBProgressHUD showHUDAddedTo:[[AppDelegate sharedInstance] window] animated:YES];
progres.mode = MBProgressHUDModeDeterminateHorizontalBar;
progres.progress = 0.0;
if (!self.sessionManager) {
_sessionManager = [[AFHTTPSessionManager alloc] initWithSessionConfiguration:config];
}
NSError *error;
NSMutableURLRequest *requet= [[AFHTTPRequestSerializer serializer] multipartFormRequestWithMethod:#"POST" URLString:#"URLTO UPLOAD" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> _Nonnull formData) {
NSError *error1;
[formData appendPartWithFileURL:[NSURL fileURLWithPath:filePath] name:#"fileUpload" fileName:path mimeType:#"image/jpeg" error:&error1];
NSLog(#"%#",error1);
} error:&error];
NSURLSessionUploadTask *uploadTask = [self.sessionManager uploadTaskWithRequest:requet fromFile:[NSURL fileURLWithPath:filePath] progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
[progres setProgress:uploadProgress.fractionCompleted];
});
NSLog(#" Progress %f",uploadProgress.fractionCompleted);
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:[AppDelegate sharedInstance].window animated:YES];
});
}];
[uploadTask resume];
}
But when i put it in background, and set active after some time. i observe that process start from where is paused.
How to continue upload task in background too?
plz on background fetch in background modes then try :)
You just need to add below code as there is bug in NSURLSessionTask for background task.
// Prepare a temporary file to store the multipart request prior to sending it to the server due to an alleged
// bug in NSURLSessionTask.
NSString* tmpFilename = [NSString stringWithFormat:#"%f", [NSDate timeIntervalSinceReferenceDate]];
NSURL* tmpFileUrl = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:tmpFilename]];
// Dump multipart request into the temporary file.
[[AFHTTPRequestSerializer serializer] requestWithMultipartFormRequest:requet
writingStreamContentsToFile:tmpFileUrl
completionHandler:^(NSError *error)
{
// Once the multipart form is serialized into a temporary file, we can initialize
// the actual HTTP request using session manager.
// Here note that we are submitting the initial multipart request. We are, however,
// forcing the body stream to be read from the temporary file.
NSURLSessionUploadTask *uploadTask = [self.sessionManager uploadTaskWithRequest:requet fromFile:tmpFileUrl progress:^(NSProgress * _Nonnull uploadProgress) {
dispatch_async(dispatch_get_main_queue(), ^{
[progres setProgress:uploadProgress.fractionCompleted];
});
NSLog(#" Progress %f",uploadProgress.fractionCompleted);
} completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:[AppDelegate sharedInstance].window animated:YES];
});
}];
[uploadTask resume];
}];
Hope this will help you. And if you have any question then please ask.

calculating total progress of downloading multiple file with AFNetworking

I want to download multiple file and then display a total progress to the user.
but the problem is here I dont know how should I calculate total progress .
here is what I do:
first I get to totalBytes Expected to receive from all of the files:
for (NSURL candidateUrl in UrlsList)
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
//setting HTTPMethod of request from GET to HEAD to download header files of requests, so we can get file size before downloaing file
[request setHTTPMethod:#"HEAD"];
getTotalImagesBytesOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[getTotalImagesBytesOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//setting totalImagesBytesExpectedToRead of all images. we use it to know how many bytes we should download for all the images
totalImagesBytesExpectedToRead += [operation.response expectedContentLength];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operationQueue addOperation:getTotalImagesBytesOperation];
}
after estimating total files size:
//downloading images
for (NSURL *imageUrl in imagesURLList) {
NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];
AFImageRequestOperation *downloadImageOperation = [AFImageRequestOperation imageRequestOperationWithRequest:request
imageProcessingBlock:nil
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, UIImage *image) {
NSLog(#"success")
}
failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error) {
NSLog(#"%#", [error localizedDescription]);
}];
[operationQueue addOperation:downloadImageOperation];
[downloadImageOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
**HOW TO CALCULATE TOTAL PROGRESS**
}
I don't know how to calculate total size!!
the values we have:
totalBytesOfAllTheFiles, totalBytesRead and totalBytesExpectedToRead for the current file which above method gives you, indexOfCurrentFile and countOfFiles.
be cautious setDownloadProgressBlock called hundreds a time.
does anyone have any idea? (sorry for bad formatting of code!)
Here is how I achieved that:
first create an NSOperationQueue:
// Add the operation to a queue
// It will start once added
//calculating images byte size
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init];
[operationQueue setMaxConcurrentOperationCount:1];
foreach (NSURL *candidateURL in urlList )
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
//setting HTTPMethod of request from GET to HEAD to download header files of requests, so we can get file size before downloaing file
[request setHTTPMethod:#"HEAD"];
AFHTTPRequestOperation *getTotalImagesBytesOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[getTotalImagesBytesOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
//setting totalImagesBytesExpectedToRead of all images. we use it to know how many bytes we should download for all the images
totalImagesBytesExpectedToRead += [operation.response expectedContentLength];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operationQueue addOperation:getTotalImagesBytesOperation];
}
//downloading images which should be downloaded
for (NSURL *imageUrl in imagesShouldBeDownlaoded) {
NSURLRequest *request = [NSURLRequest requestWithURL:imageUrl];
AFHTTPRequestOperation *downloadFileOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
//we must provide file path with file name and its extension
NSString *fileName = [self getImageName:[imageUrl absoluteString]];
downloadFileOperation.outputStream = [NSOutputStream outputStreamToFileAtPath:[[ImageManager applicationDocumentsDirectory] stringByAppendingPathComponent:fileName] append:NO];
[downloadFileOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation , id response)
{
NSLog(#"file saved");
}failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
completionBlock(NO, error);
NSLog(#"%#", [error localizedDescription]);
}];
[operationQueue addOperation:downloadFileOperation];
[downloadFileOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
totalBytesDownloaded += bytesRead;
//total progress from 0.0 to 1.0
progress = ((float)totalBytesDownloaded/(float)totalImagesBytesExpectedToRead);
if(progress == 1)
{
completionBlock(YES, nil);
}
progressCallback(progress);
}];
operationQueue works as a FIFO list, first calculate images byte size, then starts to download all images
AFNetworking3 use dispatch_group and NSProgress to achieved this:
#import <Foundation/Foundation.h>
#import <AFNetworking.h>
#interface Server : NSObject
#property (nonatomic, strong) AFHTTPSessionManager *manager;
#property (nonatomic, strong) NSProgress *progress;
- (void)downloadFilesWithUrls:(NSMutableArray <NSURL *>*)urls;
#end
#import "Server.h"
#implementation Server
- (instancetype)init
{
self = [super init];
if (self) {
_manager = [AFHTTPSessionManager manager];
_manager.requestSerializer = [AFJSONRequestSerializer serializer];
_manager.responseSerializer = [AFJSONResponseSerializer serializer];
_progress = [NSProgress progressWithTotalUnitCount:0];
[_progress addObserver:self forKeyPath:#"fractionCompleted" options:NSKeyValueObservingOptionNew context:nil];
}
return self;
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
NSLog(#"fractionCompleted:%lf",self.progress.fractionCompleted);
}
- (void)downloadFilesWithUrls:(NSMutableArray <NSURL *>*)urls {
//progress
dispatch_group_t group = dispatch_group_create();
_progress.totalUnitCount = urls.count;
[urls enumerateObjectsUsingBlock:^(NSURL * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_group_enter(group);
NSURLRequest *request = [NSURLRequest requestWithURL:obj];
NSURLSessionDownloadTask *task = [self.manager downloadTaskWithRequest:request progress:^(NSProgress * _Nonnull downloadProgress) {
} destination:^NSURL * _Nonnull(NSURL * _Nonnull targetPath, NSURLResponse * _Nonnull response) {
return [[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:NO error:nil] URLByAppendingPathComponent:[response suggestedFilename]];
} completionHandler:^(NSURLResponse * _Nonnull response, NSURL * _Nullable filePath, NSError * _Nullable error) {
NSLog(#"one task completion");
dispatch_group_leave(group);
}];
NSProgress *child = [self.manager downloadProgressForTask:task];
[self.progress addChild:child withPendingUnitCount:1];
[task resume];
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"%lf", self.progress.fractionCompleted);
});
}
#end

Resources