How to Check if an image is valid on iPhone - iphone

How to Check if an image is valid
For example, when I cached an image which was downloaded only half and failed, and this image is invalid, then I want to know it's an invalid image and download again.
(When I use the broken image, Xcode console logs an error:
ImageIO: PNG IDAT: CRC error )
So I want find a mechanism to check image's validation
for different kinds of JPEG,PNG,etc
anybody has some clues?

I would start by checking for valid header.
Secondly the footer. Usually the last 8 bytes uint values in order are
'73,69,78,68,174,66,96,130'.
This converted into a Int64 equals 5279712195050102914
This should do for a png :)
if (memcmp(img_bytes, "\211PNG", 4) != 0||OSReadBigInt64(img_bytes,(length - 8))!=5279712195050102914)
{
//Bad Data! Free your data and return or something
}

It happens because you are using same class to download image file from server and you request consequent download of same file, while it is already downloading that file, which fails the earlier one file download and results in partly downloaded file.

I suggest you to get the file size of the image & compare with the image file just downloaded.

For loading images from the network there are two obvious failure cases:
The downloaded file was not an image at all.
The downloaded data only contains part of the image data.
For the first case nil checking the return value of various UIImage methods is probably the best way to handle this error case.
The following class methods on UIImage will return nil if the image could not be initialized with the data it was provided: + (UIImage *)imageWithData:(NSData *)data and + (UIImage *)imageWithContentsOfFile:(NSString *)path.
Code that does the nil check might look like this:
UIImage *image = [UIImage imageWithContentsOfFile:pathToImage];
if (image) {
// The image was loaded. Display it
}
else {
// The image was not loaded. Redownload or display a placeholder, etc...
}
For the second failure case it is likely the result of lost connection. For this case the failure is best handled in the NSURLConnection's delegate. When downloading the image if - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error is called then the data for the request should be discarded.
Also keep in mind that didFailWithError: will not be called for 4xx or 5xx responses from the server. It is called when the connection is cut off for other reasons. It would generally be a good idea to make sure you only write image data to disk when the server has responded with a 200 OK.

Related

Showing Accurate Progress In UIProgressView While Downloading Images in iphone

I have four urls which consists images...
I'm downloding those images and placing them into documents folder..
here is my code..
-(void)viewDidLoad
{
NSMutableArray *myUrlsArray=[[NSMutableArray alloc]init];
[myUrlsArray addObject:#"http://blogs.sfweekly.com/thesnitch/steve_jobs3.jpg"];
[myUrlsArray addObject:#"http://www.droid-life.com/wp-content/uploads/2012/12/Steve-Jobs-Apple.jpg"];
[myUrlsArray addObject:#"http://2.bp.blogspot.com/-T6nbl0rQoME/To0X5FccuCI/AAAAAAAAEZQ/ipUU7JfEzTs/s1600/steve-jobs-in-time-magazine-front-cover.png"];
[myUrlsArray addObject:#"http://images.businessweek.com/ss/08/09/0929_most_influential/image/steve_jobs.jpg"];
[myUrlsArray addObject:#"http://cdn.ndtv.com/tech/gadget/image/steve-jobs-face.jpg"];
for (int i=0; i<myUrlsArray.count; i++)
{
[self downloadImageFromURL:[myUrlsArray objectAtIndex:i] withName:[NSString stringWithFormat:#"MyImage%i.jpeg",i]];
}
}
#pragma mark- downloading File
-(void)downloadImageFromURL:(NSString *)myURLString withName:(NSString *)fileName
{
UIImage *image = [[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:myURLString]]];
NSLog(#"%f,%f",image.size.width,image.size.height);
// Let's save the file into Document folder.**
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
NSString *jpegPath = [NSString stringWithFormat:#"%#/%#",documentsPath,fileName];// this path if you want save reference path in sqlite
NSData *data2 = [NSData dataWithData:UIImageJPEGRepresentation(image, 1.0f)];//1.0f = 100% quality
[data2 writeToFile:jpegPath atomically:YES];
}
NOW... I need to display a UIProgressView for above downloading progress accurately.
how can i achieve this functionality...
Can any one provide some guidelines to achieve this..
Thanks in advance...
I'd suggest you use some asynchronous downloading technique (either AFNetworking, SDWebImage, or roll your own with delegate-based NSURLSession) rather than dataWithContentsOfURL so that (a) you don't block the main queue; and (b) you can get progress updates as the downloads proceed.
I'd also suggest creating a NSProgress for each download. When your delegate method gets updates about how many bytes have been downloaded, update the NSProgress object.
You then can associate each NSProgress with a observedProgress for a UIProgressView, and when you update your NSProgress, the UI can be updated automatically.
Or, if you and a single UIProgressView to show the aggregate progress of all of the NSProgress for each download, you can create a parent NSProgress, establish each download's NSProgress as a child of the parent NSProgress, and then, as each download updates its respective NSProgress, this will automatically trigger the calculation of the parent NSProgress. And again, you can tie that parent NSProgress to a master UIProgressView, and you'll automatically update the UI with the total progress, just by having each download update its individual NSProgress.
There is a trick, though, insofar as some web services will not inform you of the number of bytes to be expected. They'll report an "expected number of bytes" of NSURLResponseUnknownLength, i.e. -1! (There are logical reasons why it does that which are probably beyond the scope of this question.) That obviously makes it hard to calculate what percentage has been downloaded.
In that case, there are a few approaches:
You can throw up your hands and just use an indeterminate progress indicator;
You can try changing the request such that web service will report meaningful "expected number of bytes" values (e.g. https://stackoverflow.com/a/22352294/1271826); or
You can use an "estimated download size" to estimate the percentage completion. For example, if you know your images are, on average, 100kb each, you can do something like the following to update the NSProgress associated with a particular download:
if (totalBytesExpectedToWrite >= totalBytesWritten) {
self.progress.totalUnitCount = totalBytesExpectedToWrite;
} else {
if (totalBytesWritten <= 0) {
self.progress.totalUnitCount = kDefaultImageSize;
} else {
double written = (double)totalBytesWritten;
double percent = tanh(written / (double)kDefaultImageSize);
self.progress.totalUnitCount = written / percent;
}
}
self.progress.completedUnitCount = totalBytesWritten;
This is a bit of sleight of hand that uses the tanh function to return a "percent complete" value that smoothly and asymptotically approaches 100%, using the kDefaultImageSize as the basis for the estimation.
It's not perfect, but it yields a pretty decent proxy for percent completion.
Your call to dataWithContentsOfURL is synchronous, meaning you don't get updates as the download is in process.
You can use a library like AFNetworking (https://github.com/AFNetworking/AFNetworking) which has callbacks to the progress of the download.
Actually a better solution is to use SDWebImage manager which will load the images in the background for you and cache them. Then the next time you use that image it will check the cache. Google it.
That way the user also doesn't have to sit around and wait while you're downloading stuff..
Then look at this other question that has some ideas on how to do a status:
How to show an activity indicator in SDWebImage
Do not use dataWithContentsOfURL, you are blocking the main thread until the data arrives.
Instead create your own connection with NSURLConnection and start listening to your delegate.
connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response: get the total data size with [response expectedContentLength].
connection:(NSURLConnection *)connection didReceiveData:(NSData *)data: This is where you do your calculations and update your UIProgressView. Something like, loadedBytes/total data size.
Good luck.

Ignoring iOS high-resolution #2x files

I have an app with a large number of images (1000+). I have a database table which contains all the filenames for these images. Images are loaded on demand based on this filename using UIImage* image = [UIImage imageNamed:...]
Due to the large number of images, I would like to programmatically test to ensure all image that are included in the database are in fact present in the project. To achieve this, I'm pulling all filenames from the table, looping over each filename, running the above code, and checking to see if image != nil. This works just fine.
The problem is that I would like to confirm that both normal resolution and high resolution (#2x) images are there. If the high-resolution file is present but the normal-resolution file is not, my code will not detect this.
Is there some way I can achieve this without having the run this process twice, once per resolution type? Can I force the SDK to ignore #2x files?
You should just use NSFileManager. It will probably be much quicker since it won't actually load the contents of the image files. For each fileName, use NSFileManager's fileExistsAtPath method to check for the image. Then, append "#2x" to the base name to check for the 2x image.
I would use NSBundle to locate the files so the UIImage doesn't have to be loaded into memory, you could use something like...
NSBundle *myBundle = [NSBundle mainBundle];
if([myBundle pathForResource:#"MyImage" forType: #"png"] == nil){
// low res image isn't there
}
if([myBundle pathForResource:#"MyImage#2x" forType: #"png"] == nil){
// high res image isn't there
}
Should be much faster...

How to Load an array into OpenFlow

I'm trying to implement openFlow in my project but I cant seem to get the images to show up on my uiview. What isnt clear to me is once I have the dictionary of image links, how do i tell AFOpenView that I want to use that dictionary object as my data source?
I've looked at the demo code and I see that when the flickr request finishes, he saves a copy of the dictionary results, counts them, and then tells OpenFlowView that there are x number of images, but what is never clear is how he tells OpenFlowView to use the dictionary with the results?
- (void)flickrAPIRequest:(OFFlickrAPIRequest *)inRequest didCompleteWithResponse:(NSDictionary *)inResponseDictionary
{
// Hold onto the response dictionary.
interestingPhotosDictionary = [inResponseDictionary retain];
int numberOfImages = [[inResponseDictionary valueForKeyPath:#"photos.photo"] count];
[(AFOpenFlowView *)self.view setNumberOfImages:numberOfImages];
}
See here: http://blog.objectgraph.com/index.php/2010/04/09/how-to-add-coverflow-effect-on-your-iphone-app-openflow/
This tutorial seems to suggest that you have to call the view's setImage method multiple times, once per image.
This tells me that the implementation is confusing and weird, but for this you have to blame the component's author.
The images are loaded on demand in the 'updateCoverImage:' method of AFOpenFlowView.m
'updateCoverImage:' calls 'openFlowView:requestImageForIndex:' in AFOpenFlowViewController.m, which uses interestingPhotosDictionary.
So, it is called on demand whenever an image needs to be loaded. It wraps an operation queue so the images are loaded outside the main thread.

Streaming JPEGs, detect end of JPEG

I have created a java server, which takes screenshots, resizes them, and sends them over TCP/IP to my iPhone application. The application then uses NSInputStream to collect the incoming image data, create an NSMutableData instance with the byte buffer, and then create a UIImage object to display on the iPhone. Screenshare, essentially. My iPhone code to collect the image data is currently as follow:
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent{
if(streamEvent == NSStreamEventHasBytesAvailable && [iStream hasBytesAvailable]){
uint8_t buffer[1024];
while([iStream hasBytesAvailable]){
NSLog(#"New Data");
int len = [iStream read:buffer maxLength:sizeof(buffer)];
[imgdata appendBytes:buffer length:len];
fullen=fullen+len;
/*Here is where the problem lies. What should be in this
if statement in order to make it test the last byte of
the incoming buffer, to tell if it is the End of Image marker
for the end of incoming JPEG file?
*/
if(buffer[len]=='FFD9'){
UIImage *img = [[UIImage alloc] initWithData:imgdata];
NSLog(#"NUMBER OF BYTES: %u", len);
image.image = img;
}
}
}
}
My problem, as indicated by the in-code comment, is figuring out when to stop collecting data in the NSMutableData object, and use the data to create a UIImage. It seems to make sense to look for the JPEG End of File marker--End of Image (EOI) marker (FFD9)--in the incoming bytes, as the image will be ready for display when this is sent. How can I test for this? I'm either missing something about how the data is stored, or about the marker within the JPEG file, but any help in testing for this would be greatly appreciated!
James
You obviously don't want to close the stream because that would kill performance.
Since you control the client server connection, send down the # of bytes in the image before sending the image data. Better yet, send down # of bytes in the image, the image data, and an easily identified serial # at the end so you can quickly verify that the data has actually arrived.
Much easier and more efficient than actually checking for the end of file marker. Though, of course, you could also just check for that after the # of bytes have been received, too. Easy enough.
Of course, all of this is going to be grossly inefficient for screensharing style purposes in all but the unusual cases. In most cases, only a small part of the screen to be mirrored actually changes with each frame. If you try to send the whole screen with every frame, you'll quickly saturate your connection and the client side will be horribly laggy and unresponsive.
Given that this is an extremely mature market, there are tons of solutions and quite a few open source bits from which you can derive a solution to fit your needs (see VNC, for example).

Get File Size of File to download using FTP

I am using the simpleFTPsample of apple. I want to display a progress bar of the file being downloaded. I understand it needs to be done in:
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
under the case:
case NSStreamEventOpenCompleted:
how do i retrive the file size from the NSInputStream?
I have also tried:
i have set:
[self.networkStream setProperty:#"YES" forKey:(id)kCFStreamPropertyFTPFetchResourceInfo];
and then:
NSLog(#"size: %#",[self.networkStream propertyForKey:(id)kCFStreamPropertyFTPResourceSize]);
but the result is null...
You will have to set kCFStreamPropertyFTPFetchResourceInfo to true. That way the CFFTPStream will send a STAT command to the FTP server to get file info, including the total size.
To get the file size you just need:
case NSStreamEventOpenCompleted: {
fileSize = [[self.networkStream propertyForKey:(id)kCFStreamPropertyFTPResourceSize] integerValue];}
By the way do you know how to get the modification date of the file in the ftp server??