接下来是问题。
当 iPhone 接收到 gzip 压缩的数据时,它会自动将其解压缩。在这种情况下,Content-Length 等于 -1。因此,如果您想继续下载 gzip 压缩数据,则制作 Range 标头不是一个好主意:您不知道 gzip 压缩数据的大小。
在我们的例子中,我们使Range 的开始等于已经下载的数据,并且在某些情况下它超过了压缩数据的大小(我什至没有说这是错误的,文件已损坏!) .所以 Web 服务器返回了 416 Requested Range not satisfiable,这就是为什么 NSURLConnection 委托的 didFailWithError 方法被调用时出现 NSURLErrorCannotDecodeRawData 错误。
详细解释和我们的解决方案
在下载管理器中我们有代码
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setRange:NSMakeRange(progress, NSNotFound)];
其中progress 是下载的数据量。它存储在数据库中以允许暂停和继续下载单个文件(例如,应用程序重新启动之间的大文件)。当我们想继续时,我们将Range 标头设置为 [progress; ∞) 间隔(从我们已经下载的偏移量接收数据)。
服务器(Apache、nginx 等)即时将 gzip 编码应用于流。这有利于减小输出文件的大小,但结果是您不知道整个 gzip 文件的大小。所以它基本上意味着你不能暂停并继续下载 gzipped 流。此外,下载的 gzip 压缩块正在接收时解压缩(NSURLConnection 委托方法 connection:didReceiveData:),因此您不会知道通过了多少 gzip 压缩数据。因此,您不会创建正确的偏移量,并且服务器将从您不想要的偏移量返回数据,并且您的结果文件首先将被损坏,其次,一旦您超过内容长度并收到416。
因此,没有一个浏览器或其他任何东西会允许您继续下载动态(根据请求生成)或 gzip 压缩的内容。如果您想暂停和继续大型压缩文件(在我们的示例中为 20 Mb JSON),请将它们设为静态并存档,或者继续 gzip 压缩它们并希望用户等到文件下载完成。
所以我们选择了第二条路径,现在如果 Content-Lenght 未知 (-1),则不要设置范围。
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
if (urlResponse.expectedContentLength != NSURLResponseUnknownLength) {
[req setRange:NSMakeRange(progress, NSNotFound)];
}