我正在使用自己的下载管理器下载一些文件。它已经运行了将近半年(甚至在发布到App Store之后)
但是昨天我有一些有趣的事情:

Error Domain=NSURLErrorDomain Code=-1015 "cannot decode raw data"
UserInfo=0x4c12e0
{
    NSErrorFailingURLStringKey=http://***/file.json.gz,
    NSErrorFailingURLKey=http://***/file.json.gz,
    NSLocalizedDescription=cannot decode raw data
    NSUnderlyingError=0x4dcec0 "cannot decode raw data"
}

一些背景知识:我有一台Web服务器,可以为我提供JSON和gzip JSON。

因此,当我尝试下载压缩文件并且仅在iPod Touch 4G(5.1.1)上时,就会发生问题!

怎么了?我该如何处理?这是Web服务器问题吗?

最佳答案

接下来是问题。
当iPhone收到压缩数据时,它将自动将其解压缩。在这种情况下,Content-Length等于-1。因此,如果您想继续下载压缩数据,则创建Range标头不是一个好主意:您不知道压缩数据的大小。
在我们的例子中,我们将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压缩块将在接收时解压缩(NSURLConnection委托方法connection:didReceiveData:),因此您将不知道传递了多少gzip数据。因此,您将无法创建正确的偏移量,并且服务器将从错误的偏移量中返回数据,并且首先,您生成的文件将被破坏;第二,一旦超出内容长度并接收到416,则该文件将被破坏。

因此,没有任何一种浏览器或任何一种浏览器可以让您继续下载动态(按要求生成)或压缩的内容。如果您想暂停并继续处理大型压缩文件(在我们的情况下为20 Mb JSON),请将其设置为静态并存档,或者继续对其进行gzip压缩,只是希望用户能等到文件下载完成。

因此,我们选择了第二条路径,如果Content-Lenght是未知的(-1),则现在不设置范围。
NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];

if (urlResponse.expectedContentLength != NSURLResponseUnknownLength) {
    [req setRange:NSMakeRange(progress, NSNotFound)];
}

10-08 07:27
查看更多