3

我正在用我自己的下载管理器下载一些文件。它运行了将近半年(甚至在将其发布到 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) 上时,问题就出现了!

怎么了?我该如何处理?是网络服务器的问题吗?

4

1 回答 1

3

接下来是问题。
当 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)];
}
于 2012-07-16T14:53:49.050 回答