如何跟踪gzip编码的网页内容下载进度?

9
我正在编写一款从网络下载内容的iPhone客户端,由于移动网络不太快,而且文件可能很大,所以我希望改进活动指示器,使用进度条来显示下载进度。
到目前为止,我使用NSURLConnection,并检查Content-Length头来查看我将要下载多少字节。然后,在-connection:didReceiveData:回调函数中,我将接收到的数据附加到我的NSMutableData对象中,因此我可以追踪已下载的内容大小与预期字节数之间的差距。
这一切都很好,直到你遇到支持gzip压缩的服务器。使用gzip压缩的服务器将广告x字节作为内容的大小。但是,由于NSURLConnection在后台执行解压缩操作,传递给didReceiveData回调函数的数据已经展开。因此,预期的下载字节数比实际接收的字节数小,比例为文件的压缩比。
这意味着进度条会溢出,因为预期的字节数比预期的早得多。当我控制服务器时,我可以为gzip内容发送特殊的头以避免NSURLConnection进行的解压缩,但我无法控制Web上的所有服务器。
是否有任何隐藏的方法使NSURLConnection报告传输字节而不是展开的字节?我是否需要编写自己的NSURLConnection以跟踪传输字节并自己解压缩gzip数据以获取准确的进度条指示器?也许有更低级别的Core Foundation API可供选择?
2个回答

5

据我所知,您应该使用NSURLResponse-expectedContentLength方法。在我的测试中,对于压缩的内容,该方法返回NSURLResponseUnknownLength(-1)。

一个解决方法是通过手动设置Accept-Encoding头来禁用发送压缩内容:

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

当然,这样做会破坏压缩内容的意义。:/ (如果有更好的解决方案(最好不涉及比NSURL*更低级别的操作),我也很想知道...)

1
不过,如果在下载实际文件之前先在 HEAD 请求中请求大小,则不会如此。HEAD 将具有 Accept-Encoding @"",但实际请求不会这样。 - Kalle
无论如何,使用另一个库(比如ASIHTTP)似乎是正确的选择(只要它能够满足原帖作者的需求)。 - Wevah
1
我正在使用一个设置了 Accept-Encoding 为空的 HEAD 请求来获取文件大小,然后使用 GET 请求(这里不会对 Accept-Encoding 进行任何处理)来获取文件内容,就像 @Kalle 建议的那样。这种方法非常有效,可以正确计算进度,而且不需要使用任何第三方库。 - bk138

1
使用ASIHTTPRequest库。这是我对几乎所有“如何在iPhone上成为Web客户端”的问题的通用答案,但在这种情况下特别有用。
检查这个:
-(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使许多强大的网络操作变得非常简单。我是一个忠实的粉丝。

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


我接受这个库作为答案,它无法在NSURLConnection级别上完成,你必须更深入地挖掘CFNetwork API。 - Grzegorz Adam Hankiewicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接