接下来是问题。
当 iPhone 接收到 gzip 压缩的数据时,它会自动将其解压缩。在Content-Length
这种情况下 等于-1
。因此,如果您想继续下载 gzipped 数据,则制作Range
标头不是一个好主意:您不知道 gzipped 数据的大小。
在我们的例子中,我们将开始Range
等于已经下载的数据,并且在某些情况下它超过了 gzip 压缩数据的大小(我什至没有说这是错误的,文件已损坏!)。所以 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)];
}