我正在使用自己的下载管理器下载一些文件。它已经运行了将近半年(甚至在发布到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)];
}