This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
iOS download and save image inside app
I want to download a file. I found this
[NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"]]
My questions are:
Where does the file saved?
By using threads, is it any possible to build a status bar for downloads?
Are there any way to change the memory(internal/external) to save the file?
Now I am using
NSData *dataImage = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"]];
downloadStatus.text =#"size: %zd", malloc_size(dataImage);
The result is always 32. Shouldn't that be the size of the actual image?
The file is not saved. It's loaded/retrieved and converted into an NSData object.
Yes. However, if you're doing this you should look at NSURLConnection and particularly the NSURLConnectionDataDelegate protocol. You'll need to asynchronously download the file and get the callbacks into the delegate to be able to update your status bar. Or you could use a 3rd party networking library to simplify the whole thing, but it's good to understand what's going on behind the scenes.
Yes. You can save the NSData object as a file when it's downloaded. If you were using Cocoa (not iOS) you could use NSURLDownload to download the file directly.
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDownloadProgressDelegate:myProgressIndicator];
[request startSynchronous];
NSLog(#"Max: %f, Value: %f", [myProgressIndicator maxValue],[myProgressIndicator doubleValue]);
And you can also follow this link to make more underastanding :)
http://allseeing-i.com/ASIHTTPRequest/How-to-use
You can assign the downloaded data to a variable and show it in an UIImageView as follows.
NSData *dataImage = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://lasp.colorado.edu/home/wp-content/uploads/2011/03/suncombo1.jpg"]];
self.imageViewYours.image = [UIImage imageWithData:dataImage];
Try This Code Worked for Me. The Image will save to your PhotoAlbum.
-(IBAction)save_Image:(id)sender
{
UIImageWriteToSavedPhotosAlbum([UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:[NSString stringWithFormat:#"Your URL"]]]], self, #selector(image:didFinishSavingWithError:contextInfo:), nil);
[self performSelectorOnMainThread:#selector(imageDownloaded) withObject:nil waitUntilDone:NO ];
}
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
if (error != NULL)
{
// handle error
}
else
{
// handle ok status
}
}
- (void)imageDownloaded
{
// network animation off
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// do whatever you need to do after
}
Since you're new to Obj-C, I'd first get familiar with NSURLConnection and NSURLConnectionDataDelegate (protocol). Once you feel comfortable that you know what's going on, you can easily switch to a networking library such as AFNetworking to simplify things.
Related
I am using this code for displaying the image from URL to UIImageview
UIImageView *myview=[[UIImageView alloc]init];
myview.frame = CGRectMake(50, 50, 320, 480);
NSURL *imgURL=[[NSURL alloc]initWithString:#"http://soccerlens.com/files/2011/03/chelsea-1112-home.png"];
NSData *imgdata=[[NSData alloc]initWithContentsOfURL:imgURL];
UIImage *image=[[UIImage alloc]initWithData:imgdata];
myview.image=image;
[self.view addSubview:myview];
But the problem is that its taking too long time to display the image in imageview.
Please help me...
Is there any method to fast the process...
Instead of dispatch_async, Use SDWebImage for caching the images.
This is best I have seen...
The problem of dispatch_async is that if you lost focus from image, it will load again. However SDWebImage, Caches the image and it wont reload again.
The answers given to me on my own question Understanding the behaviour of [NSData dataWithContentsOfURL:URL] inside the GCD block does makes sense.So be sure that if you use [NSData dataWithContentsOfURL:URL] inside the GCD(as many developers do these days) is not a great idea to download the files/images.So i am leaning towards the below approach(you can either use NSOperationQueue).
Load your images using [NSURLConnection sendAsynchronousRequest:queue:completionHandler: then use NSCache to prevent downloading the same image again and again.
As suggested by many developers go for SDWebimage and it does include the above strategy to download the images files .You can load as many images you want and the same URL won't be downloaded several times as per the author of the code
EDIT:
Example on [NSURLConnection sendAsynchronousRequest:queue:completionHandler:
NSURL *url = [NSURL URLWithString:#"your_URL"];
NSURLRequest *myUrlRequest = [NSURLRequest requestWithURL:url];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[NSURLConnection sendAsynchronousRequest:myUrlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
{
if ([data length] > 0 && error == nil)
//doSomething With The data
else if (error != nil && error.code == ERROR_CODE_TIMEOUT)
//time out error
else if (error != nil)
//download error
}];
Use Dispatch queue to load image from URL.
dispatch_async(dispatch_get_main_queue(), ^{
});
Or add a placeholder image till your image gets load from URL.
I m using sendSynchronousRequest to get the data from the server. I know that synchronous will wait until the data received for that request.
But the problem comes when user by mistake enters some non-existing url and than tries to get response. In this case, if user goes in to background and than comes into foreground it shows only black screen. It only shows status bar. Also its not showing any background application. I have to press Home button to come out of my application.
On simulator, After 1+ minute it shows me the message that "Request time out" (No crash).
On Device, within 1 min application get crashes.
Any suggestion. Any Help. This is really a serious issue in my app.
Thanks.
Just like Julien said, the watchdog is killing your app. To answer some questions:
why does this happen only on the simulator?
Because when you're debugging the watchdog leaves your app alone, it can take time.
why does this happen only when the user enters a wrong url?
Because of the system timeout, the system will keep trying for 60 secs if it can't find a server.
so the problem is synchronous vs asynchronous?
No, the problem is the thread, you can do the same operation in a background thread, just don't do it on the main thread and the watchdog will leave you alone.
why is the screen black when the app comes up?
Remember, you are making blocking stuff on the main thread, the thread that draws...
Hope that was all. Let me know if I missed something.
Why not setting a timeout for your connection?
NSString *urlString = TEST_CONNECTION;
NSError *error = nil;
NSHTTPURLResponse *response = nil;
NSURLRequest *request = [NSURLRequest
requestWithURL:[NSURL URLWithString:urlString]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:5.0];
NSData *conn = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
This should release the synchronous waiting after a number of seconds, which should solve your problem without going with an asynchronous call (which sometimes isn't the proper solution)
I know this works properly because this is how I check if I am connected to a certain VPN (where reachability flags totally fail).
you should take a look to this article: https://developer.apple.com/library/ios/#qa/qa1693/_index.html
iOs contains a watchdog, if your application is blocked to much time on an operation on the main thread, this one will be killed. (for more details about Watchdog: http://en.wikipedia.org/wiki/Watchdog_timer)
So if you want to download something, don't download it on the main thread.
RELATE
UIImage *image = [self.imgCache objectForKey:urlString];
if(!image){
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:urlString] cachePolicy:NSURLRequestReturnCacheDataElseLoad timeoutInterval:60.0];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURLResponse *response = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:nil];
NSLog(#"%#",response);
UIImage *img = [UIImage imageWithData:data];
//
if(img)
{
dispatch_sync(dispatch_get_main_queue(), ^{
[self.imgCache setObject:img forKey:urlString];
completionBlock(img);
});
}
});
}
else{
completionBlock(image);
}
use ASIHttpRequest class instead of NSURLConnection , its nothing but wrapper around NSURLConnection and has very simple callbacks , you can also set time to complete a request. Please go through this link for more info http://allseeing-i.com/ASIHTTPRequest/
I think you first have to test user data whether it is correct or not and than only if it is correct, sends the request otherwise prompt user that "please enter correct data"...
or
when your parsing of data in response failed. You can also make protocol delegate method i.e FinishWithError so that you come up with your last UI.
Try this one:
#import "ASIHTTPRequest.h"
//In a method
[self performSelectorInBackground:#selector(DownLoadImageInBackground:) withObject:imgUrlArr];
-(void) DownLoadImageInBackground:(NSArray *)imgUrlArr1
{
NSURL * url = [Image URL];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setDelegate:self];
[request startAsynchronous];
}
-(void)requestFailed:(ASIHTTPRequest *)request
{
NSLog(#"URL Fail : %#",request.url);
NSError *error = [request error];
// you can give here alert too..
}
-(void)requestFinished:(ASIHTTPRequest *)request
{
NSData *responseData = [request responseData];
UIImage *imgInBackground = [[UIImage alloc]
initWithData:responseData];
[imageView setImage: imgInBackground];
}
This might help you: I am also loading a number of images at one time, so images that have no proper data show a black screen. To avoid this, try to resize your imageview.
You could check the reachability of the URL before starting the request.
Apple has Reachability Methods to do so. But its easier to use a wrapper. E.g. ASIReachability.
I think the application crashing because you does not get any data when user enters wrong URL and you are using this 'returned' nil NSData to do stuffs.
I think this will fix your problem
NSData *data=[NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
if(data!=nil){
///
} else {
NSLog(#"NO DATA");
}
My app is downloading packages of images from a server. It's an array of direct links (20-50 files) from XML.
How do I make sure the whole set of images is fully downloaded?
How do I add a condition to cancel whole download (and remove all already downloaded files) if app was closed with iPhone button? (such methods are in AppDelegate while my whole download code is in some downloadviewcontroller.m)
Anything else I have to worry about while downloading several files? (5-10 MB in total)
The code I currently use isn't very safe in case of download interrupting or app closing. In background thread I'm calling this method for each file:
(BOOL) loadImageFromURL:(NSString *)url withName:(NSString *)filename toFolder:(NSString *)folder {
NSURL *link = [NSURL URLWithString:url];
NSFileManager *manager = [NSFileManager defaultManager];
NSString *filepath = [folder stringByAppendingPathComponent:filename];
if ([manager fileExistsAtPath:filepath]) {
return YES;
}
else
{
UIImage *image = [[UIImage imageWithData:[NSData dataWithContentsOfURL:link]] retain];
NSData *data = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];
if ([data length] <= 0)
[image release];
return NO; // no data
else
{
[data writeToFile:filepath atomically:YES];
[image release];
return YES;
}
}
}
Use nsoperation to do so
check out blow link..
http://www.cimgf.com/2008/02/16/cocoa-tutorial-nsoperation-and-nsoperationqueue/
now on august 2012 try to search wwdc 2012 video no 211 to learn nsoperation.
u can use block to do so.
[aNsque addExecutionBlock:^{ ...code... }];
where aNsque is nsblockoperation.
Dont use the synchronous call dataWithContentsOfURL. Instead look at how to use the asynchronous method of NSURLConnection, – initWithRequest:delegate:
You can then cancel the request with [connection cancel]; Also you will not have to run it on another thread because it is already asynchronous.
As far as having multiple requests running, you probably have a few options. One idea would be to create an abject that starts the NSURLConnection and parses the response, then create an array of theses objects.
I'm getting an image over HTTP, using NSURLConnection, as follows -
NSMutableData *receivedData;
- (void)getImage {
self.receivedData = [[NSMutableData alloc] init];
NSURLConnection *theConnection = // create connection
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
-(void)connectionDidFinishLoading:(NSURLConnection *)connection {
[connection release];
UIImage *theImage = [UIImage imageWithData:receivedData];
}
Usually it works just fine, but sometimes I'm seeing this get logged - : Corrupt JPEG data: premature end of data segment
At this point, the image does not completely render. I'll see maybe 75% of it, and then the lower right hand corner is a grey box.
Any ideas on how to approach fixing this? Am I constructing my image improperly?
Your HTTP code looks correct. You might want to log the size of the receivedData once it's done loading and compare it to the expected size of the image on the server. If it's the expected size, then maybe the image itself is corrupted on the server.
ASI-HTTP can fix this problem.
NSURL *coverRequestUrl = [NSURL URLWithString:imageStringURL];
ASIHTTPRequest *coverRequest = [[ASIHTTPRequest alloc] initWithURL:coverRequestUrl];
[coverRequest setDelegate:self];
[coverRequest setDidFinishSelector:#selector(imageRecieved:)];
[appDelegate.queue addOperation:coverRequest];
[appDelegate.queue go];
My queue variable in appDelegate is ASINetwork queue object. Because I send asynchronous request, so I use it.
- (void)imageRecieved:(ASIHTTPRequest *)response
{
UIImage *myImage = [UIImage imageWithData:[response responseData]];
}
I fixed this problem by using an NSMutableDictionary.
NSMutableDictionary *dataDictionary;
In my loadData function, I define my data:
NSMutableData *receivedData = receivedData = [[NSMutableData alloc] init];
Then I load the data into my dictionary where the key is [theConnection description] and the object is my data.
[dataDictionary setObject:receivedData forKey:[theConnection description]];
That way in the delegates, I can look up the correct data object for the connection that is passed to the delegate and save to the right data instance otherwise I end up with the JPEG munging/corruption problem.
In didReceiveData, I put:
//get the object for the connection that has been passed to connectionDidRecieveData and that object will be the data variable for that instance of the connection.
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
//then act on that data
[theReceivedData appendData:data];
Similarly, in didReceiveResponse, I put:
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
[theReceivedData setLength:0];
And in connectionDidFinishLoading:
NSMutableData *theReceivedData = [dataDictionary objectForKey:[connection description]];
img = [[UIImage alloc] initWithData:theReceivedData];
And this seems to work very well. By the way, my code is based on Apple's tutorial for NSUrlConnection with the addition of an NSMutableDictionary to keep track of individual connections. I hope this helps. Let me know if you want me to post my full image handling code.
I have seen this also. If you save the data to a file and then read the data back into an image, it works perfectly. I suspect there is http header information in the jpeg image data.
Hope someone finds a solution to this because the save to file workaround sucks.
// problem
UIImage *newImage = [UIImage imageWithData:receivedData];
// crappy workaround
[receivedData writeToFile:[NSString stringWithFormat:#"a.jpg"] atomically:NO];
UIImage *newImage = [UIImage imageWithContentsOfFile:#"a.jpg"];
Copying the NSData content using receivedData = [NSData dataWithBytes:receivedData.bytes length:receivedData.length] may be helpful too (and it's more efficient than saving to and reading from the disk).
A possible reason for this is that the original receivedData object does not retain its content (e.g. when created using [NSData dataWithBytesNoCopy:length:]) and you try to read them after they are freed.
This is likely when you encounter this problem on another thread from the thread that created the NSData object.
but i want dowload a video from URL and to rename the file of that video before saving it.i use UISaveVideoAtPathToSavedPhotosAlbum ?any help..what i have to do ?
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL
URLWithString:#"www.xyz.com/image.mp4"]];
You would do something like this:
- (void) downloadVideo
NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:#"http://www.xyz.com/image.mp4"]];
NSString *tempPath = [NSString stringWithFormat:#"%#/%#", NSTemporaryDirectory(), temp.mp4];
[imageData writeToFile:tempPath atomically:NO];
UISaveVideoAtPathToSavedPhotosAlbum (# tempPath, self, #selector(video:didFinishSavingWithError: contextInfo:), nil);
);
- (void) video: (NSString *) videoPath
didFinishSavingWithError: (NSError *) error
contextInfo: (void *) contextInfo {
NSLog(#"Finished saving video with error: %#", error);
}
There are two things you need to take note of:
Make sure you specify the scheme of the URL. This isn't a browser, it is not going to attempt to autofill http and guess for you.
If this is on the main thread it will cause a synchronous stall while dataWithContentsOfURL: happens. If the main thread stalls for more than 20 seconds the phone will assume the app has gotten stuck in an infinite loop and kill it. So if this code is on the main thread you need to either move it onto a background thread or move to an asynchronous download using NSURLConnection.
I had the same problem, solved it changing the extension of the file being saved from .mpg to .mp4. apparently UISaveVideoAtPathToSavedPhotosAlbum expects a correct extension, though from the documentation it is not clear to me that this is a requirement:
http://developer.apple.com/library/ios/#DOCUMENTATION/UIKit/Reference/UIKitFunctionReference/Reference/reference.html