I am using following code in order to download zip file using following code with only one running operation at a time
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:TempUrl]];
operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:[NSString stringWithFormat:#"%#.zip",model.iD]];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:path append:NO];
[operation setUserInfo:[NSDictionary dictionaryWithObject:model forKey:#"model"]];
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {
NSLog(#"%lu",(unsigned long)bytesRead);
NSLog(#"%lu",(unsigned long)totalBytesRead);
NSLog(#"%lu",(unsigned long)totalBytesExpectedToRead);
}];
Following code handles pausing and resuming download on app home button press or screen lock
// Notification invokes these methods
-(void)appEntersBg { // Application enters background
if([[self downloadQueue] operationCount]) {
[[[[self downloadQueue]operations]firstObject] pause];
}}
-(void)appEntersForground { // application enters forground
if([[self downloadQueue] operationCount]) {
[[[[self downloadQueue]operations]firstObject] resume];
}
}
When I start a download i get proper values in bytes read and bytes expected
but weird values when app enters Background and then again enters forground.
Like I am downloading a file of size 10MB
I start download and DOWNLOADEDSIZE reaches 4MB.
and EXPECTEDDOWNLOADSIZE size is 10MB
Now I press home button and downloading PAUSES.
Launch app
App Enters Forground and downloading RESUMES
Now EXPECTEDDOWNLOADSIZE is 6MB (10-4MB)
and DOWNLOADEDSIZE is same as previous 4MB.
File is downloaded properly but How will I show the download progress correctly after
multiple times app getting into background and foreground
Any suggestions or help
I am working on iOS 7 and AFNetworking 2.
Related
I am downloading a file with AFNetworking using an AFHTTPRequestOperation.
I want the user to be able to resume the download after it fails (from either a time out, or a lost connection...).
How would I do this?
I have read that AFDownloadRequestOperation does this, but this is not officially part of AFNetworking, and it is not up to date.
How do I resume a failed download?
So I was able to resume by doing this:
First I make my app remember the "totalBytesRead" and "totalBytesExpectedToRead".
[operation setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead) {...}
Then, when the download fails, I have a retry button. When the user clicks the button, I call this code
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:#"www.myfileURL.com/file.mp4" cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10];
NSString* range = [NSString stringWithFormat:#"bytes=%lli-%lli", totalBytesRead, totalBytesExpectedToRead];
[request setValue:range forHTTPHeaderField:#"Range"];
operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.outputStream = [NSOutputStream outputStreamToFileAtPath:myPath append:YES];
Make sure you properly set the http header for range in the format "bytes=fromBytes-totalBytes". An example would be "bytes=3200-12000"
Make sure that when you make the new operation, you change the output stream to APPEND:YES. This is so your downloaded bytes append to the previously downloaded ones.
I have .json file in my Xcode project that contains much information.
I can't fetch it each time because it's really big. But I'd like to update that file once a week in my app. Is it possible?
Maybe you can make a local notification that is fired once a week, and then download the file. Or you can even send push notifications to tell the app to download.
To download a file (in this case and unzip it using SSZipArchiver and AFNetworking for the download):
NSString *url = [NSString stringWithFormat:#"someurl"];
NSString *directoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:url]];
NSString *filePath = [directoryPath stringByAppendingPathComponent:[NSString stringWithFormat:#"nameofthefile.zip"]];
zipPath = filePath;
NSLog(#"DIRECTORY WHERE THE FILES ARE SAVED-->%#",directoryPath);
AFURLConnectionOperation *operation1 = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation1.outputStream = [NSOutputStream outputStreamToFileAtPath:filePath append:NO];
[operation1 setDownloadProgressBlock:^(NSUInteger bytesRead, long long totalBytesRead, long long totalBytesExpectedToRead)
{
NSLog(#"-----> %f", (float)totalBytesRead / totalBytesExpectedToRead);
self.hud.progress = (float)totalBytesRead / totalBytesExpectedToRead;
}];
[operation1 setCompletionBlock:^()
{
[SSZipArchive unzipFileAtPath:zipPath toDestination:directoryPath overwrite:YES password:nil error:nil delegate:self];
}];
[operation1 start];
Here is some information: How to create local notifications Stackoverflow
You cannot replace the file in your Xcode project. You can only read resources from the application bundle. If you want to write you need to first copy the file to the application documents directory and modify it there.
Now you can download even large files in the background and replace the file in the writable directory when you are done.
Further remarks:
Think about how you could devise the web service to just send incremental changes. That seems so much more reasonable. Also, rather than parsing the content of a huge JSON file into memory, you might be better off using Core Data.
I am struck in a big problem. What I am trying to do is getting data from the server but the server in a single hit give all the data which is very large in quantity and I am doing all my process on the main thread, So there are around 400-500 images in the form of URL which I am saving in document directory in the form of NSData. So in the dubug navigator when the memory consumption reached around 80-90 mb then my application crashed and showing the following error:-
mach_vm_map(size=135168) failed (error code=3)
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
2015-01-23 17:10:03.946 ArchaioMobileViewer[853:148470] *** Terminating app due to uncaught exception 'NSMallocException', reason: 'Attempt to allocate 262144 bytes for NS/CFData failed'
I am Using ARC but still I am getting the memory problem. This is my code `-
(void)downloadDocumentsFromServer:(NSDictionary *)documentsList IsUpdate:(BOOL)isUpdate;
{
//Main Target(22)
BusinessLayer* bLL = [[BusinessLayer alloc]init];
FileManager* downloadImages = [FileManager alloc];
for(NSDictionary* inspDocumentResult in documentsList)
{
FloorDocument* floorDocument = [[FloorDocument alloc]init];
floorDocument.docID = [inspDocumentResult objectForKey:#"docID"];
floorDocument.buildingID = selectedBuildingID;
floorDocument.clientID = clientID;
NSDictionary* documentArray = [inspDocumentResult objectForKey:#"Document"];
floorDocument.docType = [[documentArray objectForKey:#"Type"] stringByTrimmingCharactersInSet:
[NSCharacterSet whitespaceAndNewlineCharacterSet]];
floorDocument.docScale = [documentArray objectForKey:#"Scale"];
floorDocument.docDescription = [documentArray objectForKey:#"DocDesc"];
//floorDocument.floor = [bLL getFloorNameByDocIDAndBuildingID:selectedBuildingID DocID:floorDocument.docID];
floorDocument.floor = [inspDocumentResult objectForKey:#"Floor"];
NSLog(#"%#",[inspDocumentResult objectForKey:#"hiResImage"]);
[downloadImages downloadInspectionDocuments:[inspDocumentResult objectForKey:#"hiResImage"] ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID];
NSLog(#"Floor %# - High Res Image copied for %#",floorDocument.floor,floorDocument.docID);
//Download the Low Res Image
NSString* lowResImage = [inspDocumentResult objectForKey:#"lowResImage"];
[downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:#"lowResImage"];
//Copy the Quarter Size File
lowResImage = [lowResImage stringByReplacingOccurrencesOfString:#"LowRes" withString:#"LowRes4"];
[downloadImages downloadInspectionDocumentsLowRes:lowResImage ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID LowResName:#"lowResImage4"];
NSLog(#"Floor %# - Low Res Images copied for %#",floorDocument.floor,floorDocument.docID);
//Download the tiles
NSArray* tiles = [inspDocumentResult objectForKey:#"lsUrls"];
for(NSString* tile in tiles)
{
#autoreleasepool {
NSArray* tileNameArray = [tile componentsSeparatedByString:#"/"];
if(tileNameArray.count > 0)
{
NSString* destTile = [tileNameArray objectAtIndex:tileNameArray.count-1];
destTile = [destTile stringByReplacingOccurrencesOfString:[NSString stringWithFormat:#".%#",floorDocument.docType] withString:#""];
NSLog(#"TileName:%#",destTile);
[downloadImages downloadInspectionDocumentsTiles:tile ImageName:floorDocument.docID FileType:floorDocument.docType Folder:selectedBuildingID TileName:destTile];
}
}
}
NSLog(#"Floor %# - Tiles Image copied for %#",floorDocument.floor,floorDocument.docID);
NSLog(#"Downloading Documents Tiles For %# Completed at %#",floorDocument.docID,[bLL getCurrentDate]);
[bLL saveFloorDocuments:floorDocument IsUpdate:isUpdate];
// downloadImages=nil;
}
bLL = nil;
}
please help me out in this problem.`
This is the code which I am using inside the DownloadInspectionDocuments:-
-(void)downloadInspectionDocuments:(NSString *)url ImageName:(NSString *)imageName FileType:(NSString*)fileType Folder:(NSString*)folder
{
#autoreleasepool
{
NSString* source =[FileManager getInspectionDocumentsFolder];
//Lets get the destination folder
NSString *destination = [NSString stringWithFormat:#"%#/%#/%#",source,folder,imageName];
[self createFolder:destination CreateSubFolders:true];
NSString *filePath = [NSString stringWithFormat:#"%#/%#.%#",destination,imageName,fileType];
NSFileManager* fm = [[NSFileManager alloc]init];
if(![fm fileExistsAtPath:filePath])
{
NSData *data1 = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
[data1 writeToFile:filePath atomically:YES];
}
}
// return [NSString stringWithFormat:#"%#.%#",imageName,fileType];
}
ARC isn't garbage collection: it will insert the memory management code (retain/release) for you but you still need to make sure you are not using up too many resources (in the same way you would in non-ARC code).
You are running this large loop on the main thread, so any memory being consumed is not going to be freed until the next run loop.
You need to break this function down into smaller steps that can be carried out in stages.
For now, if there isn't too much memory consumed for a single iteration of the outer-loop of the function you can add an autorelease pool at that level (I see you have on on the inner loop)
for(NSDictionary* inspDocumentResult in documentsList)
{
#autoreleasepool {
.... remaining code goes here
}
}
and it will at least drain what it can each iteration.
Given that you are downloading a large number of files and will be relying on network connectivity I would recommend performing the downloads asynchronously though. If you haven't already, check out AFNetworking to simplify this. This will give you much more control over your resources than you are getting now with a resource-intensive blocking call on the main thread.
You can save yourself a lot of work by following davbryn's and Andrea's suggestions to use AFNetworking and stream the file. Basically, don't put the whole file in memory and then write it to disk, write to disk as you get the bytes from the network. This should reduce the pressure on memory. For example:
- (void)downloadFile:(NSString *)urlString {
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString]];
NSString *destinationPath = [NSDocumentDirectory() stringByAppendingPathComponent:#"some-file-name"];
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
[operation setOutputStream:[NSOutputStream outputStreamToFileAtPath:destinationPath append:NO]];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"Super duper awesome!");
// Maybe start another download here?
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error downloading file: %#", error);
}];
[operation start];
}
So then all you need to do is generate the list of things to download and in your success block start downloading another file.
If I put download then suddenly I Lock the Iphone with Passcode, The downloading progress is getting stopped with in a fraction of seconds. the Dropbox service is not continuing to downloading the progress.
Here this method is for download operation.
-(void)downloadFile:(DBMetadata*)file
{
if (!file.isDirectory)
{
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
localPath = [documentsPath stringByAppendingPathComponent:file.filename];
[[self restClientForDownload] loadFile:file.path intoPath:localPath];
}
}
you have to call rest client methods from Main thread. If you're starting the connection from the the main thread, just use GCD to create and start the connection on the main thread -
dispatch_async(dispatch_get_main_queue(), ^
{
NSURLConnection *downManager = [[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES];
[downManager start];
});
I am trying to cancel an Asynchronous Download using ASIHTTPRequest with no joy. I am downloading a movie to disk on the appearance of a ViewController. What I want is when the user click close to dismiss the view controller I want to cancel the download. Here is the code I have so far:
Making the request:
-(void)retrieveLatestFile{
NSURL *url = [NSURL URLWithString:[appDelegate.urlToLoad stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
ashiRequest = [[ASIHTTPRequest requestWithURL:url] retain];
[ashiRequest setUsername:#"Appidae"];
[ashiRequest setPassword:#"Dubstance1"];
[ashiRequest setDelegate:self];
[ashiRequest startAsynchronous];
[ashiRequest setDownloadProgressDelegate:progressView];
NSLog(#"Value: %f", [progressView progress]);
if ([progressView progress]==1) {
[self hideInfoPane];
}
//NSLog(#"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *tempPath = [NSString stringWithFormat:#"%#/download/%#/%#", documentsDirectory, appDelegate.languageFolder,appDelegate.filename];
NSLog(#"Path: %#", tempPath);
[ashiRequest setDownloadDestinationPath:tempPath];
}
Cancel the the downloading:
- (IBAction)closeDocDisplay:(id)sender {
// Cancels an asynchronous request
[ashiRequest clearDelegatesAndCancel];
[self dismissModalViewControllerAnimated:YES];
}
Everything is working, the download is fine and the movie plays properly but when I dismiss the View Controller the request is not canceled and my file is still downloading (network activity indicator spins on the status bar. Even the ASIHTTP delegate error doesn't get called
Try using
ashiRequest.delegate = nil;
[ashiRequest cancel];
This should work.
I figured out what was wrong. I was using Reachability to call my retrieveLatestFileMethod. That's fine if Reachability is being used just once in the app, maybe from the delegate. But I had a few instances of it so it keept calling the same functions and downloads.