NSURLConnection and Content-Encoding - iphone

I have an NSURLConnection which is downloading a web page. I get the Content-Length which is much smaller than the actual size of the file.
I noticed that the header also says:
Content-Encoding:gzip
I guess the size returned by Content-Length is the compressed size, however the NSData returned by NSURLConnection has been decompressed.
Firstly does NSURLConnection automatically decompress it?
And how do I get the length of either the uncompressed file (instead of Content-Length)

If you need a progress bar, why not try ASIHTTPRequest? It has a number of built in features that help manage a progress bar.

If what you're trying to do is make some sort of progress bar or progress indicator for an NSURLConnection download, try the below code. It works like a charm in a few of my apps.
- (void) connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
// append the new data to receivedData (a global variable).
[receivedData appendData:data];
// calculate number of kilobytes
int kb = [receivedData length] / 1024;
// update a label, or some other visula thingy
self.downloadProgressLabel.text = [NSString stringWithFormat:#"Downloaded\n%d kB", kb];
}

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.

iphone - skip a section of data when downloading?

I am currently using NSURLConnection to download some date from a database through a php script on a server.
The connection works fine and data is received correctly. However I have a problem when I come to parse the data.
I am currently trying to use the NSXMLParser to parse the data however this is failing with Error code 4, I think this is because what I retrieve is not entirely XML. If it is entirely XML it works.
Here is an example of the data retrieved :
43534545-45345345-34534554|iPhone emulator|<provdoc>
<characteristic type="P1">
...
</characteristic>
</provdoc>
And what I'd like to do is split the data into:
43534545-45345345-34534554
and:
iPhone emulator
and
<provdoc>
<characteristic type="Profile1">
...
</characteristic>
</provdoc>
So Im guessing that I should do that in the following function where I take in the data, I need to know how I split the data into the above three sections?
So I end up with two strings, the first one with the numbers and the second with the iPhone emulator bit and then the data that can be send through the NSXMLParser.
Can anyone point me in the right direction as yo what I need yo accomplish that?
-(void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data{
[self.responseData appendData:data];
}
Convert the final NSData to an NSString:
NSString *someString = [[NSString alloc] initWithData:hashedData encoding:NSUTF8Encoding];
Then you'll need to start splitting up that string. Here's an example of getting the first portion:
NSRange end = [someString rangeOfString:#"<"];
NSString *str = [someString substringWithRange:NSMakeRange(0, end.location)];
Then split up the first one:
NSArray *initialItems = [str componentsSeparatedByString:#"|"];
You should be able to figure out the rest with the above techniques.

NSData over GameKit and EXC_BAD_ACCESS strange problem

I'm trying to stream video data over a peer-to-peer connection created with GameKit. I have a method that receives an NSData object and uses it to draw a video stream onto a CALayer:
- (void)recieveVideoFromData:(NSData *)data;
Here are the first few lines of that method which convert the NSData to CMSampleBufferRefs and begins processing:
CMSampleBufferRef imgData = (CMSampleBufferRef)data.bytes;
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(imgData);
CVPixelBufferLockBaseAddress(imageBuffer,0);
Now, when I feed the video stream from the local camera into this method as follows, everything works just fine and the video stream displays on screen:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
NSData *data = [[NSData alloc] initWithBytes:sampleBuffer length:malloc_size(sampleBuffer)];
[self recieveVideoFromData:data];
}
But, when I send a stream of those NSData packets over a peer-to-peer connection and receive them in the following fashion, I get an EXC_BAD_ACCESS error:
- (void)match:(GKMatch *)match didReceiveData:(NSData *)data fromPlayer:(NSString *)playerID {
[self recieveVideoFromData:data];
}
Using the debugger, I learned that the bad access occurs on this line:
CVPixelBufferLockBaseAddress(imageBuffer,0);
I have no idea why the NSData sent over the network should be any different than the NSData sent from another method on the same device. I have checked that the data received over the network is being received at the same interval and is the same length (336 bytes) as the data produced on the local device. I also checked the retain count of the data object is 1 before it is used. It seems that the imageBuffer variable is somehow getting lost.
A couple of questions:
Is casting data.bytes to a CMSampleBufferRef the right way to go about unpacking NSData?
How do I assert that the data being received is actually a CMSampleBuffer object? I want to protect my code but I'm not sure how to accomplish a class verification for Core Foundation classes.
Thanks in advance!
Why are you 'unpacking' (that's not unpacking) your CMSampleBuffer by casting the bytes of NSData? That's never going to work, because CMSampleBuffer is not a continuous block in memory.
You have to retrieve all the relevant data from a CMSampleBuffer yourself before sending, stuff it into and NSData object, and reasemble it on the other side via
OSStatus CMSampleBufferCreate (
CFAllocatorRef allocator,
CMBlockBufferRef dataBuffer,
Boolean dataReady,
CMSampleBufferMakeDataReadyCallback makeDataReadyCallback,
void *makeDataReadyRefcon,
CMFormatDescriptionRef formatDescription,
CMItemCount numSamples,
CMItemCount numSampleTimingEntries,
const CMSampleTimingInfo *sampleTimingArray,
CMItemCount numSampleSizeEntries,
const size_t *sampleSizeArray,
CMSampleBufferRef *sBufOut
);
The data types in this function might give you a hint what you want to extract from the CMSampleBuffer when packing your data.
This is probably not the entire answer, but your use of malloc_size seems like a huge red flag to me. This seems a non-portable extension, not governed by anything like ANSI, ISO or POSIX, and I have some doubts on how it might behave if passed a buffer that didn't come from malloc. It seems like a sketchy thing to rely on. (I would say if it's come to calling malloc_size you're already doing something wrong as a C coder, since C is all about knowing how big your buffers are upfront and not relying on non-portable libc functions to do your buffer-size-tracking work for you.)

NSURL Loading data percentage Value

I need to read NSURL Loading data percentage value.
That is, I want to read how many percentage amount of data is loading on URL Loading time.
Suppose a URL take 30 seconds to loading data completely, I want to know in 15th sec how many percentage of data is loading in my String, and 20th sec how many percentage of data is loading in my String, like this I need.
First of all, you will have to use NSURLConnection. In its delegate's implementation include the following method:
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse *)response {
long long contentLength = [response expectedContentLength];
...
}
Note that the only way to know how many bytes the server is going to return is to retrieve the value from its HTTP response. If you find that expectedContentLength returns -1 (and you should be prepared for that), the server does not return a meaningful value for the downloaded content size, perhaps because it doesn't support the feature. In such a case you should resort to alternative ways to get the byte size.

iphone download several files

In my app i need to download several plist. 
to download a plist i use the NSURLconnection 
in my code i use an UIAlertView with a UIActivityIndicator then when the download is finished i add a button to the alert to dismiss it. 
To download the plist i use somewhere in my code an NSURL set to the adresse where the plist is, next i set a NSURLRequest with the url cache policy and a timeout interval. 
Then i set my NSMutableData to the NSURL connection with a NSURLRequest. 
In the delegate didReceiveData: i append data to my mutable data object, in the didFailWithError: i handle error. And finaly in the connectionDidFinishLoading  i serialize my data to a plist so i can write to file my plist, and release my alertview. 
My problem is : how can i do if i have sevetal file to download because the connectionDidFinishLoading is called each time my NSURLConnection is finished but i want to release my UiAlert when everything is finished. But when the first plist is downloaded my code in the connectionDidFinishLoading will fire. 
here is my code : 
in the view did load : 
// set the UiAlert in the view did load 
NSURL *theUrl = [NSURL URLWithString:#"http://adress.com/plist/myPlist.plist"];
NSURLRequest *theRequest = [NSURLRequest requestWithURL:theUrl cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0];
self.plistConnection = [[ NSURLConnection alloc] initwithRequest:theRequest delegate:self startImmediatly:YES];
//plistConnection is a NSURLConnection
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { 
[incomingPListData appendData:data];
}
-(void)connection:(NSURLConnection *)connectionDidFailWithError:(NSError *)error {
// handle error here 
}
-(void)connectionDidFinisloading:(NSURLConnection *) connection { 
NSPropertyListFormat format;
NSString *serialErrorString; 
NSData *plist = [NSPropertyListSerialisation propertyListFromData:incomingPlistData mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&serialErrorString];
if (serialErrorString) {//error}
else { // create path and write plist to path}
// change message and title of the alert
so if i want todownload an another file  where do i put the request the connection and how can i tell the didFinishLoading to fire code when all my file are downloaded. 
thanks to all 
You can iterate over an array of resources you wish to download, and allocate a request for each of them. It is possible to set a tag to a connection (e.g. the index of the URL in the array) which you can evaluate in the connectionDidFinishLoading. If you hold the information, which requests are sent and which are finished, you can easily see, if all files have been loaded.
I think unset provided a good answer. I understand that you don't get it (you will someday), as I remember myself being new to programming and such.
I therefore provide another, much simpler option to evaluate if all downloads did finish.
you simply use a counter that you define in your .h file,
int activeDownloads;
in your implementation (.m) File, wherever you start all your downloads, set activeDownloads to zero before any of your downloads start
activeDownloads = 0;
before you start a download you increase the number of activeDownloads
++activeDownloads;
if a download finishes or fails you decrease the same countervariable
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { --activeDownloads;}
(i didn't write down the method that gets called if a download fails...
also everytime a connection finishes or fails you have to check if the one that finished or railed was the last one.
you do that by simply checking if activeDownloads is equal to zero. If that is the case, you can add the "close" Button to your AlertView.
The drawback of this solution is, that you're unable to track which connection succeeded and which failed. (well, you are, but activeDownloads don't help you with that)
hope i could help
cheers