Objective-c Check file size from URL without downloading - ios

I need to check the size of file from a URL. I get the file size perfectly when downloading file with AFNetworking.
AFHTTPRequestOperation *operation = [client HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// Success Callback
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// Failure Callback
}];
and get file size in another block
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
}];
But i need to check file size before initiating download request, so that i can prompt to user. I have also tried another method
NSURL *url = [NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"];
NSData *data = [NSData dataWithContentsOfURL:url];
NSLog(#"original = %d", [data length]);
But it blocks the UI, coz it download all data to calculate its size.
Is there any way to check file size before downloading? Any help is appreciated.

If the server supports it you can make a request to just get the headers (HEAD, as opposed to GET) without the actual data payload and this should include the Content-Length.
If you can't do that then you need to start the download and use expectedContentLength of the NSURLResponse.
Basically, create an instance of NSMutableURLRequest and call setHTTPMethod: with the method parameter set to #"HEAD" (to replace the default which is GET). Then send that to the server as you currently request for the full set of data (same URL).

here is the code:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:candidateURL];
[request setHTTPMethod:#"HEAD"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"Content-lent: %lld", [operation.response expectedContentLength]);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];

Related

Ios-Afnetworking multiple download using selector method

Handle single download via afnetworking is good my question is that how handle multiple click on different button then it call this method then process break previous.
it is bcoz suppose several button hit at time then it confuse to download. how handle multiple download in selector method,if in array of batch download then it's easy but through which how .
-(void)downloadimagefromserver:(UIButton *)sender
{
int index =(int) sender.tag;
historyclass *class1 = [messages objectAtIndex:index];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:arrayOfStringsfinal[1]]];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
{
NSLog(#"success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[operation start];
}

Downloading a large number of images (1500+) efficiently with AFNetworking 2

I am building an app that downloads a large number of images, sometimes 1500-5000 images depending on what the user requests. To do this, I am using AFNetworking 2. At first, I was just looping through all of my URLs and then making a request for each one.
for (NSString *url in urls) {
NSURLRequest *request = [NSURLRequest requestWithURL:url];
AFHTTPRequestOperation *requestOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
requestOperation.responseSerializer = [AFImageResponseSerializer serializer];
[requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
completion(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failure(error);
}];
[requestOperation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
double percentDone = (double)totalBytesRead / (double)totalBytesExpectedToRead;
progress(percentDone);
}];
[requestOperation start];
}
But, after I got to about 900 downloads/requests I would start to get the following error:
The request timed out
I am assuming this error came directly from AFNetworking.
What is the best and most efficient way to make a large number of download requests like this without timing out? Should I be using dispatch_group to batch the requests as outlined here?
Or, should I use a recursive method that will download one image at a time, and only start the next request once the first one finishes?
Try this
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url];
[request setTimeoutInterval:600]; /* 10 minutes */
But it would be best solution if you just download an archive of images and then unpack it.

AFNetworking and id reponseObject NOT working and not able to recover the content

After few days of job I am not able to solve this issue, I really need your help because I am completely locked, I start to be crazy!!!!!!. I have a project in Objective c for iOS where I get data from my server to put in my application. I have some trouble to recover and save data from JSON.
I would like to use "id responseObject", and save and use the content in another area in my project. Each time I try to use the following method and use "id responseObject" outside of "setCompletionBlockWithSucess" the "id responseObject" is (null), how can I do ?
NSURL *URL = [NSURL URLWithString:#"…."];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
[[NSOperationQueue mainQueue] addOperation:op]
It sounds like you don't quite understand what scope is for variables. This project may be too advanced for you if that's the case. I urge you to read into scope + blocks to get a better understanding of what's going on.
What is happening is the setCompletionBlockWithSuccess is actually a block of code that gets executed if the URL request is a success. This means that responseObject is not immediately executed! It's being passed back some time after and you get access to it within setCompletionBlockWithSuccess. So that's why it's nil outside of the block.
To do what you're wanting is very simple. You need to read responseObject within the setCompletionBlockWithSuccess and set it to another variable that you have access to. Or you can immediately send it to another class to parse/save.
I haven't tested it, but I believe this should work with a simple JSON response. If not, use operation.responseString instead
NSString *jsonResponse;
NSURL *URL = [NSURL URLWithString:#"…."];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];
AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:request];
op.responseSerializer = [AFJSONResponseSerializer serializer];
[op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
jsonResponse = responseObject;
}
...

AFNetworking AFHTTPRequestOperation block is never called

I'm using AFNetworking to send a multipart form to a web-server, and i'm having some trouble with my AFHTTPRequestOperation. It's success and failure blocks are never called, after i start it.
Here is my code (a resume of it)
NSMutableURLRequest *request = [[ServerAPI sharedClient] multipartFormRequestWithMethod:#"POST" path:postUrl parameters:nil constructingBodyWithBlock: ^(id <AFMultipartFormData> formData) {
[formData appendPartWithFileData:picture.picture_data name:#"InputFile" fileName:picture.name mimeType:#"image/jpg"];
}];
AFHTTPRequestOperation *operation = [[ServerAPI sharedClient] HTTPRequestOperationWithRequest: request success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success");
} failure: ^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
[operation setUploadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSLog(#"%f", (totalBytesRead / (float) totalBytesExpectedToRead));
}];
[[ServerAPI sharedClient] enqueueHTTPRequestOperation:operation];
I can see the logs of the progress, but success and failure blocks are never called.
picture.picture_data is a NSData initialized with a UIImageJPEGRepresentation(image, 0.7)
ServerAPI is a subclass of AFHTTPClient, and sharedCliend is a singleton method.
Which are the reasons for AFNetworking don't call my blocks, not even with an proper error message?
Thank you all!
Edit
I do a get request with the same URL just before this one, and it works as usual. The URL i'm using is: part/_layouts/UploadEx.aspx?List=%7BD432BF97-7175-40C1-8E0D-27D8661CBC90%7D&RootFolder=%2Fpwa%2Fpart%2FLibrary&Source=http%3A%2F%2Fwww%2Emysite%2Ecom%2Fpwa%2Fpart%2FLibrary%2FForms%2FAllItems%2Easpx&IsDlg=1
In your code, check your postUrl . The BaseURL+postURL must be valid. Try upload image using normal web browser using URL BaseURL+postURL.
Edit
method HTTPRequestOperationWithRequest:success:failure: does not work for file uploading, but works for json/html fetching.
Try use
AFHTTPRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:request];
[operation setUploadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSLog(#"%f", (totalBytesRead / (float) totalBytesExpectedToRead));
}];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Success");
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
[[ServerAPI sharedClient] enqueueHTTPRequestOperation:operation];

track usage of data sent and received through my app

How to track usage of data sent and received through my app?
I just want to record bytes sent and received when my app is running. If I can get separate info for Wifi and Cellular network then it would be great but its not a priority.
I know how to find total usage of device - https://stackoverflow.com/a/8014012/427969
Also, I know I can use Instruments to collect network activity data but I want to record this data in my app so need a programmatic way to do this.
I tried to search this but all I find is device's network usage and not a particular app's usage.
The following is the screenshot of Whatsapp's Settings -> Usage page, which would give a better idea of what I am trying to do:
I am using AFNetworking for HTTP request and response as follows:
NSData* requestData = [NSJSONSerialization dataWithJSONObject:info options: NSJSONWritingPrettyPrinted error:&error];
if(error != nil) {
NSLog(#"Error: converting JSON: %#, %#", error, error.userInfo);
}
[request setHTTPMethod:#"POST"];
[request setValue:#"application/x-www-form-urlencoded; charset=UTF-8" forHTTPHeaderField:#"Content-Type"];
[request setValue:[NSString stringWithFormat:#"%d", [requestData length]] forHTTPHeaderField:#"Content-Length"];
[request setHTTPBody: requestData];
/*
################### ----------------- ###################
WILL [requestData length] BE THE NUMBER OF BYTES SEND ???
################### ----------------- ###################
*/
NSLog(#"data bytes: %d", [requestData length]);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL .....];
AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest: request
success: ^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
} failure: ^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON) {
}];
[operation start];
I have updated my question.
Can somebody please answer: Is [requestData length] the number of bytes SEND for one request?
There are several Methods depending on which class you are using to Download with AFNetworking
AFHTTPRequestOperation for example has the following method:
setDownloadProgressBlock:^(NSInteger bytesRead, NSInteger totalBytesRead, NSInteger totalBytesExpectedToRead)
For the other way around, there is a method like this:
setUploadProgressBlock:^(NSInteger bytesWritten,long long totalBytesWritten,long long totalBytesExpectedToWrite)
With this to methods you should try to keep track of all your uploaded and downloaded data.
AFJSONRequestOperation is a subclass of AFHTTPRequestOperation, so those method should work in either class.
And please be aware that only sending a "json request" to a webserver doesn't mean that you are not downloading. For sure you have to get the content - the json - which would be your data downloaded.
Further you qre questioning if [requestData length]is telling you the proper size sent to the server. That's not the exact size, because within your requestData you do not have the additional Headers for the HTTP request, therefore your size will be a little bit smaller than the original bytes sent.
Every time one of the methods above is executed you should add the result of bytes read and add it to the bytes you read before this method was executed. For sure you have to save the current bytesRead permanently in coredata for example or any other persistence store.
I used the methods mentioned by Alexander as follows:
AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest: request];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
// success
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
// failure
}];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
// bytes received - saved bytesRead variable to NSUserDefaults
}];
[operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
// bytes sent - saved bytesWritten variable to NSUserDefaults
}];
[operation start];

Resources