Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I track the download progress of gzip encoded web content?

I'm writting an iPhone client that downloads stuff from the net. Since the cellular network is not so fast, and files might be big, I wanted to improve upon the activity spinner with a progress bar.

So far so good, I'm using NSURLConnection and checking the Content-Length header to see how many bytes I will download. Then, in the -connection:didReceiveData: callback I append received data to my NSMutableData object and there I can track the size of the downloaded content so far vs expected bytes.

This all works until you have a server which supports gzip compression. A server using gzip compression will advertise x bytes as the size of the content. However, since NSURLConnection does the decompression behind the scenes, the data passed to the didReceiveData callback is already expanded. Therefore, the expected downloaded bytes are smaller than the actual received bytes, in the proportion of the compression ratio for the file.

This means that the progress bar overflows, since the expected bytes count is reached much earlier than expected. Something I could do when I control the server is send special headers for the gzip content to avoid the decompression made by NSURLConnection, but I can't control all the servers on the web.

Is there any hidden method for NSURLConnection to report the transferred bytes rather than expanded bytes? Do I have to code my own NSURLConnection to track transferred bytes and decompressed the gzip data myself for an accurate progress bar indicator? Maybe there is an alternative lower level Core Foundation API?

like image 806
Grzegorz Adam Hankiewicz Avatar asked Aug 05 '10 10:08

Grzegorz Adam Hankiewicz


People also ask

How do I see gzip compression in Chrome?

Verify Your Compression In your browser: In Chrome, open the Developer Tools > Network Tab (Firefox/IE will be similar). Refresh your page, and click the network line for the page itself (i.e., www.google.com ). The header “Content-encoding: gzip” means the contents were sent compressed.

How do I test gzip compression?

Double click on the file and select headers. Under 'Response headers' you are looking for the 'Connection-Encoding' field, it will say gzip if it is enabled. NOTE: By default, Deflate is enabled on all of our servers.

What is content Encoding gzip?

Gzip is a file format and software application used on Unix and Unix-like systems to compress HTTP content before it's served to a client.

How does browser gzip work?

Using gzip on the WebWhen a browser with gzip support sends a request, it adds “gzip” to its Accept-Encoding header. When the web server receives the request, it generates the response as normal, then checks the Accept-Encoding header to determine how to encode the response.


2 Answers

IIRC, you should be using the -expectedContentLength method of NSURLResponse, which, in my testing, returns NSURLResponseUnknownLength (-1) for gzipped content.

One workaround would be to disable sending of gzipped content by manually setting the Accept-Encoding header:

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:yourURL];
[request setValue:@"" forHTTPHeaderField:@"Accept-Encoding"];

but of course, this defeats the point of gzipping content. :/

(If there's a better solution (that hopefully doesn't involve going lower-level than the NSURL* stuff) I'd love to know about it, too…)

like image 107
Wevah Avatar answered Sep 24 '22 15:09

Wevah


Use the ASIHTTPRequest library. That's my generic answer to pretty much all "how do I be a web client on the iPhone" questions, but it's particularly useful in this case.

Check this:

-(void)viewDidLoad {
    ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:myNSURLObject];
    request.delegate = self;
    request.downloadProgressDelegate = self.myUIProgressViewInstance; 
      //it'll update that progress bar with a percentage complete automatically!
      //or give it any custom class that responds to -progress!
    [request startAsynchronous];
}

- (void)requestFinished:(ASIHTTPRequest *)request
{
    //do whatever to process the data you just got
}

ASIHTTP makes many powerful network operations very simple. I'm a big fan.

http://allseeing-i.com/ASIHTTPRequest/

like image 21
Dan Ray Avatar answered Sep 21 '22 15:09

Dan Ray