Reputation: 50717
I am trying to download multiple images from a URL stored in an XML feed. Getting the image urls from the XML is working correctly. However, the NSURLConnection is creating empty files, but the data is received as noted in NSLog. In connectionDidFinishLoading:(NSURLConnection *)connection
, the data and correct bytes are received, the problem is how do I make the receivedData write to the correct file.
-(void)parsingComplete:(XMLDataSource*)theParser
{
/* iterate through the Categories and create the
sub-directory if it does not exist
*/
for (int i = 0; i < [categories count]; i++) {
NSString *cat = [NSString stringWithFormat:@"%@/%@",BASE_DIR,[[categories objectAtIndex:i] objectForKey:@"name"]];
NSString *catName = [[categories objectAtIndex:i] objectForKey:@"name"];
NSArray *catArray = [[categories objectAtIndex:i] objectForKey:@"images"];
/* create the sub-direcotry naming it the #category# key */
if (![FILEMANAGER fileExistsAtPath:cat]) {
[FILEMANAGER createDirectoryAtPath:cat withIntermediateDirectories:NO attributes:nil error:nil];
}
//NSLog(@"\n\nCategory: %@",cat);
for (int x = 0; x < [catArray count]; x++) {
//NSLog(@"Image: %@",[[catArray objectAtIndex:x] objectForKey:@"imageUrl"]);
/* download each file to the corresponding category sub-directory */
fileOut = [NSString stringWithFormat:@"%@/%@_0%i.jpg",cat,catName,x];
NSURLRequest *imageRequest =
[NSURLRequest requestWithURL:[NSURL URLWithString:[[catArray objectAtIndex:x] objectForKey:@"imageUrl"]]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30.0];
NSURLConnection *imageConnection = [[NSURLConnection alloc] initWithRequest:imageRequest delegate:self];
int counter = 0;
//BOOL result = NO;
if(imageConnection)
{
NSLog(@"Counter: %i",counter++);
receivedData = [[NSMutableData data] retain];
/*result = */[receivedData writeToFile:fileOut atomically:YES];
}
/*
if (!result) NSLog(@"Failed"); else NSLog(@"Successful");
*/
}
}
}
#pragma mark NSURLConenction
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// release the connection, and the data object
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
// inform the user
NSLog(@"Connection failed! Error - %@ %@",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// do something with the data
// receivedData is declared as a method instance elsewhere
NSLog(@"Succeeded! Received %d bytes of data",[receivedData length]);
NSString *aStr = [[NSString alloc] initWithData:receivedData encoding:NSASCIIStringEncoding];
// release the connection, and the data object
//[receivedData release];
}
Upvotes: 2
Views: 5316
Reputation: 2099
You can use a CustomURLConnection
with Tag to name de images before they download.
With this code you can make a customURLConnection
, name it when you make the request, and ask for the name of the image in the connectionDidFinishLoading:
#import <Foundation/Foundation.h>
@interface CustomURLConnection : NSURLConnection
{
NSString *tag;
}
@property (nonatomic, retain) NSString *tag;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)aTag;
@end
#import "CustomURLConnection.h"
@implementation CustomURLConnection
@synthesize tag;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate startImmediately:(BOOL)startImmediately tag:(NSString*)aTag
{
self = [super initWithRequest:request delegate:delegate startImmediately:startImmediately];
if (self) {
self.tag = aTag;
}
return self;
}
- (void)dealloc
{
[tag release];
[super dealloc];
}
@end
Then make the connection, a custom url connection in your parsingComplete with:
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:yourURL];
[request setTimeoutInterval:3000.0];
CustomURLConnection *connection = [[CustomURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES tag:imageTag];
Now you can take the imageName
with the CustomURLConnection
tag, and save it in the connectionDidFinishLoading:
CustomURLConnection *urlConec = (CustomURLConnection*)connection;
NSMutableData *dataFromConnection = [self dataForConnection:urlConec];
and this is the code for the function dataForConnection:
- (NSMutableData*)dataForConnection:(CustomURLConnection*)connection
{
NSMutableData *data = [receivedData objectForKey:connection.tag];
return data;
}
Hope that helps.
Upvotes: 1
Reputation: 64002
You have to wait until the connection tells you it has finished before you can write the data. The connection is handled on another thread; if you try to access the data immediately on the original thread as you're doing, there won't be anything in it.
You should move the writeToFile:
call to the end of connectionDidFinishLoading:
, or to another method that you call from there. That's the first point where you know that the data has all been collected.
I'd also suggest creating the NSMutableData
instance in didRecieveResponse:
, so that you know that it is available at the correct time. That will be more readable/understandable. You can think of the delegate methods as a collective "scope" -- the data is only used inside of them, so it should be created inside of one of them.
In reply to your comment:
One possibility, since you have so much that needs to be done around this one download, and don't seem to be touching the GUI, is to run the whole parsingComplete:
method on a background thread, and using +[NSURLConnection sendSynchronousRequest:returningResponse:error:]
. This way your code will just wait until the data comes back, in one piece, and you can write it immediately after the sendSynchronous...
call returns.
NSError * err;
NSURLResponse * response;
NSData * receivedData = [NSURLConnection sendSynchronousRequest:imageRequest
returningResponse:&response
error:&err];
if( !receivedData ){
/* Handle error */
}
/* Check response */
BOOL result = [receivedData writeToFile:fileOut atomically:YES];
/* check result, etc. */
Upvotes: 3