Iphone JPEG datastream contains no image using NSURLConnection - iphone

I was previously downloading images for my app by using dataWithContentsOfURL to download a jpg then writeToFile to save it.
I recent;y started using an NSURLConnetion to do the same, but now I am getting the follwoing errors and a crash:
Corrupt JPEG data: 87 extraneous bytes
JPEG datastream contains no image
I know these images are not corrumpt, as the app was downloading them fine using the previous method. Here is my code:
-(void) downloadSave {
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString *tempString = [[NSString alloc]initWithFormat:#"http://www.mysite.com/%#.jpg",chartFileName];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:tempString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
mutableData = [[NSMutableData data] retain];
self.image = nil;
NSLog(#"connection exists");
[NSURLConnection connectionWithRequest:theRequest delegate:self];
} else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Connection Error" message:#"There was an error contacting the chart servers. Please try again." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
[activityIndicator stopAnimating];
}
// NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
// NSUserDomainMask, YES);
// NSString *docsPath = [paths objectAtIndex:0];
// NSString *downloadPath = [[[NSString alloc]initWithFormat:#"http://www.mysite.com/%#.jpg",chartFileName]autorelease];
// downloadedChartData = nil;
[pool drain];
[pool release];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
NSLog(#"got to connection did receive response");
[mutableData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[mutableData appendData:data];
// NSLog(#"got some data, total: %i",mutableData.length);
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
// release the connection, and the data object
// [connection release];
// receivedData is declared as a method instance elsewhere
// self.mutableData = nil;
// 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",[mutableData length]);
[connection release];
// release the connection, and the data object
NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *docsPath = [paths objectAtIndex:0];
self.image = nil;
NSString *savePath = [[[NSString alloc]initWithFormat:#"%#/%#.jpg",docsPath, chartFileName]autorelease];
[mutableData writeToFile:savePath atomically:YES];
self.mutableData = nil;

You are initializing and starting two NSURLConnections with the same delegate. As your delegate methods do not check which connection called them you are mixing up the bytes of two times your image in one NSMutableData instance.
// Creates, initializes and starts an instance of NSURLConnection
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
...
// Creates, initializes and starts another instance of NSURLConnection, with same request and delegate
[NSURLConnection connectionWithRequest:theRequest delegate:self];
Both connections message the same implementations on the same delegate instance, which means their data is written into the same NSMutableData in random order.
I would suggest to simply get rid of the line:
[NSURLConnection connectionWithRequest:theRequest delegate:self];
Another thing: why are you using an autorelease pool in downloadSave? If you call it from the main Thread you have only one NSURLRequest autoreleased in that pool. If you call it from an other Thread you have to take care, that the runloop of that thread is setup and running, or you wouldn't receive any delegate callbacks at all.

Related

Unable to download file to app directory in iPhone

I am new to iPhone,
I am currently developing an iPhone app and would like to implement the ability to download file from the url. I have created the UIWebView, when i click on download link in the webview download will start and i am saving that file to a specified folder in the documents directory. but i am unable to see my downloaded file.
Here is my code snippet,
//CAPTURE USER LINK-CLICK in UIwebView.
- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:DUrl]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
receivedData = [[NSMutableData data] retain];
} else {
NSLog(#"Inform the user that the connection failed.");
}
return YES;
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data1
{
[receivedData appendData:data1];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[receivedData length]);
DirPath=[self MyApplicationDocumentDirectory];
[receivedData writeToFile:DirPath atomically:YES];
UIAlertView* Alert = [[UIAlertView alloc] initWithTitle:#"Download Complete !"
message:nil delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[Alert show];
[Alert release];
// release the connection, and the data object
[connection release];
[receivedData release];
}
Any help will be appriciated.
EDIT:
BOOL success =[[NSFileManager defaultManager] fileExistsAtPath:MyDirPath];
if (success)
{
UIAlertView* innerAlert = [[UIAlertView alloc] initWithTitle:#"Already downloaded."
message:#"Do you want to Downlaod again ?" delegate:self
cancelButtonTitle:nil
otherButtonTitles:#"Yes",#"No",nil];
[innerAlert show];
[innerAlert release];
}
where to write this condition ?
EDIT Check whether downloaded file already exits in doc dir before writing(saving) downloaded data like this:
NSArray *dirContents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:DirPath error:nil];
BOOL fileExists = NO;
for(NSString *fileName in dirContents)
{
NSString *filePath = [DirPath stringByAppendingPathComponent:fileName];
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
if([receivedData isEqualToData:fileData]) //your receivedData here
{
fileExists = YES;
}
}
if(fileExists)
{
NSLog(#"File exists");
}
else
{
NSLog(#"File does not exists");
}
U forgot provide fileName for writing data:
DirPath=[self MyApplicationDocumentDirectory];
NSString *filePath = [DirPath stringByAppendingPathComponent:#"yourFileName"];
[receivedData writeToFile:filePath atomically:YES];

How to call thread for any function in iphone

please help me on this how to start thread on background in iphone on every when application is start for
in my sqlite database table i have one column sync status .there i have 3 value 0,1,3.i have to call these values on database table.0 means if application has synced data to server.if any data in my database is updated the sync status changes to 1 and 3 means data is being send to server i.e syncing is in progress.
// Create the request.
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.apple.com/"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
// Create the NSMutableData to hold the received data.
// receivedData is an instance variable declared elsewhere.
receivedData = [[NSMutableData data] retain];
} else {
// Inform the user that the connection failed.
}
Then implement delegate methods of NSURLConnection
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
if(!receivedData){
receivedData = [[NSMutableData alloc]init];
}
[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:NSURLErrorFailingURLStringErrorKey]);
}
- (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]);
// release the connection, and the data object
[connection release];
[receivedData release];
}
Hope this helps
You can use NSThread class method to start the new thread.e.g.
[NSThread detachNewThreadSelector:#selector(newThread) toTarget:self withObject:nil];
///////
-(void)newThread{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
///your code
[pool release];
}
for more read Thread Programming guide
starting threads in background easily done with NSInvocation, all you need to know is here
there is GCD but each to their own

Multiple NSURLCoonections in same View using iphone sdk

Can any one of you post the code snippet, example tutorial on handling the multiple
NSURLConnections from the same viewController using cocoa Touch framework....
Thanks for all your future help.....
I handled multiple NSUrlConnections using a NSMutableDictionary which keeps track of which instance of NSMutableData a particular NSURLConnection should save its result to.
At the beginning of my class I define:
NSMutableDictionary *dataDictionary;
Then in my loadData method, I have:
// Create the request
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:currentCam]
cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
timeoutInterval:30];
// create the connection with the request
// and start loading the data
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
NSMutableData *receivedData = receivedData = [[NSMutableData alloc] init];
//keep track of this connection by adding it to subViewDictionary and dataDictionary with appropriate objects.
[dataDictionary setObject:receivedData forKey:[theConnection description]];
}
else {
NSLog(#"ERROR DOWNLOADING WITH NSURLCONNECTION");
}
[theConnection release];
I use [theConnection description] as my key and an instance of MSMutableData as the object in my dictionary, so later I can look up which instance goes with a particular connection. If you fail to do this you can have issues with data corruption (multiple connections can all save their data to the same variable).
Then, I define the following NSURlConnection delegate methods:
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSMutableData *)data
{
//look up in dictionary to find out which recievedData instance to use.
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
// Append the new data to receivedData.
[theReceivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
//Lookup in the dictionary to find the correct instance of recievedData to put image into.
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
[theReceivedData setLength:0];
[activityIndicator stopAnimating];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
{
// inform the user that there was an error
NSLog(#"Connection failed! Error - localizedDescription:%# NSURLErrorFailingURLStringErrorKey:%#",
[error localizedDescription],
[[error userInfo] objectForKey:NSURLErrorFailingURLStringErrorKey]);
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
[theReceivedData release];
//remove keys for this connection since it did not load.
[dataDictionary removeObjectForKey:[connection description]];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
//look up correct instance of recievedData in teh dictionary for this connection
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
NSLog(#"Succeeded! Received %d bytes of data",[theReceivedData length]);
//---do stuff with data here//
[theReceivedData release];
}
There is a good tutorial on NSURlConnection here for a single connection. My code is based on that with the addition of the NSMutableDictionary to keep track of each NSUrlConnection and each NSMutableData instance.
I hope that makes sense and is helpful!
See this link for the answer. It wraps nsurlconnection with asihttp, which makes your life much easier.
Does ASIHTTP support multi threads?

iPhone NSURLConnection - delegates not working

I am trying to get an NSURLConnection to work in my app. I have followed apple's code almost exactly but it doesn't seem to work. The NSURLConnection is inside of a method called, downloadSave. This method runs properly through the end, and my log indicates, "Connection Exists" - however nothing happens after that as if none of the delegate methods get called.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
NSString *tempString = [[NSString alloc]initWithFormat:#"http://www.myWebsite.com/%#.jpg",chartFileName];
NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:tempString]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
NSURLConnection *theConnection=[[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
mutableData = [[NSMutableData data] retain];
self.image = nil;
NSLog(#"connection exists");
} else {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"Connection Error" message:#"There was an error contacting the servers. Please try again." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
[alert release];
[activityIndicator stopAnimating];
}
[pool drain];
[pool release];
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
NSLog(#"got to connection did receive response");
[mutableData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[mutableData appendData:data];
NSLog(#"got some data were at %i",mutableData.length);
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error
{
[connection release];
// receivedData is declared as a method instance elsewhere
self.mutableData = nil;
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
NSLog(#"Succeeded! Received %d bytes of data",[mutableData length]);
//more code follows to display the downloaded image
}
The only thing that appears in the log is: "Connection Exists"
I can only guess by your code that downloadSave in called in a separate thread as you have an NSAutoReleasePool (not saying thats what your doing but its likely). NSURLConnection can only respond to the delegate methods in the main thread when it is initialised in the main thread.
As NSURLConnection already is a threaded delegate call you shouldn't need to create it in thread. If you need to thread it for some reason you should be able to use
NSError *error;
NSURLResponse *response;
NSData *connectionData = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];
And that should return the data to the child thread.

Get URL File Size before parsing

Me again! I am using NSURL to get a file then parse it. I have been looking for a couple of hours now on a progressbar i am trying to implement in my app. I know I first need to get the file size, then keep updating how much data i've downloaded as I continue to pull. I have seen the example using "ASIHTTPRequest" but is there a way to do it with what I already have?
Here is where I start the download.
-(void)parseNewData {
//start network activity spinner and release controller when done
parserDone = NO;
[root downloadIcon];
//create pool to avoid memory leak
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// get the XML path and start parsing
NSURL *pathURL = [NSURL URLWithString:#"http://www.mysite.com/myfile.xml"];
NSXMLParser *parser = [[NSXMLParser alloc] initWithContentsOfURL:pathURL];
[parser setDelegate:self];
[parser parse];
//drain pool
[pool drain];
[pool release];
}
Can someone point me in the right direction on how to get file size, then how to update how much I've downloaded. Thanks in advance!
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
{
[webData setLength: 0];
CGFloat size = [[NSString stringWithFormat:#"%lli",[response expectedContentLength]] floatValue];
NSLog(#"Size : %f",size);
}
Above code will give you total size
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
{
[webData appendData:data];
totalDownloadedData += [data length]; // global integer
NSLog(#"Status : %d/%f",totalDownloadedData,size);
}
Above code will show you current status of downloading
You need to use NSURLConnection if you want to get the file size and the progress. You get delegate methods which you can use to monitor progress. The didSendBodyData: delegate method tells you how much data there is in bytes. The connectionDidFinishLoading is where you uget receivedData to use in your NSXMLParser code.
NSURLRequest *theRequest = [NSURLRequest requestWithURL:URL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10.0];
NSURLConnection *theConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self];
if (theConnection) {
receivedData = [[NSMutableData data] retain];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response{
// This method is called when the server has determined that it
// has enough information to create the NSURLResponse.
// It can be called multiple times, for example in the case of a
// redirect, so each time we reset the data.
// receivedData is an instance variable declared elsewhere.
[receivedData setLength:0];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error{
// release the connection, and the data object
// inform the user
NSLog(#"Connection failed! Error - %# %#",
[error localizedDescription],
[[error userInfo] objectForKey:NSErrorFailingURLStringKey]);
[connection release];
// receivedData is declared as a method instance elsewhere
[receivedData release];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
// Append the new data to receivedData.
// receivedData is an instance variable declared elsewhere.
[receivedData appendData:data];
}
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite{
- (void)connectionDidFinishLoading:(NSURLConnection *)connection{