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"
}

少し背景:JSONとgzip圧縮されたJSONを提供するWebサーバーがあります。

そのため、gzip圧縮されたファイルをiPod Touch 4G(5.1.1)でのみダウンロードしようとすると、問題が発生します。

何が起こっている?どうすれば処理できますか?Webサーバーの問題ですか?

4

1 に答える 1

3

問題は次でした。
iPhoneはgzip圧縮されたデータを受信すると、自動的に解凍します。そしてContent-Lengthこの場合はに等しいです-1。したがって、gzipで圧縮されたデータのダウンロードを続行する場合は、Rangeヘッダーを作成することはお勧めできません。gzipで圧縮されたデータのサイズがわかりません。
私たちの場合、Rangeすでにダウンロードされたデータと同等のデータを開始し、場合によってはgzip圧縮されたデータのサイズを超えました(間違っているとは言えませんが、ファイルが破損しています!)。そのため、 Webサーバーが返さ416 Requested Range not satisfiableれ、NSURLConnectionデリゲートのdidFailWithErrorメソッドがNSURLErrorCannotDecodeRawDataエラーで呼び出されたのはそのためです。

詳細な説明とソリューション

ダウンロードマネージャーにはコードがありました

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
[req setRange:NSMakeRange(progress, NSNotFound)];

progressダウンロードされたデータの量はどこにありますか。DBに保存され、単一のファイル(たとえば、アプリケーションの再起動の間に大きなファイル)のダウンロードを一時停止して続行できるようにします。続行する場合はRange、[progress; ∞)間隔(ダウンロード済みのオフセットからデータを受信するため)。

サーバー(Apache、nginxなど)は、オンザフライでストリームにgzipエンコーディングを適用します。これは出力ファイルのサイズを小さくするのに適していますが、結果としてgzip圧縮されたファイル全体のサイズがわかりません。つまり、基本的には、gzip圧縮されたストリームのダウンロードを一時停止して続行できないことを意味します。さらに、ダウンロードされたgzip圧縮されたチャンクは受信時に解凍されているため(NSURLConnectionデリゲートメソッドconnection:didReceiveData:)、どのくらいのgzip圧縮されたデータが渡されたかはわかりません。したがって、正しいオフセットを作成せず、サーバーは意図しないオフセットからデータを返し、結果のファイルは最初は破損し、次にコンテンツの長さを超えてを受信すると破損します416

したがって、動的な(要求に応じて生成された)コンテンツまたはgzip圧縮されたコンテンツのダウンロードを続行できるブラウザなどはありません。大きな圧縮ファイル(この場合は20 Mb JSON)を一時停止して続行する場合は、静的にしてアーカイブするか、gzipを続行して、ファイルがダウンロードされるまでユーザーが待機することを期待します。

したがって、2番目のパスを選択し、不明な場合は範囲​​を設定しませんContent-Lenght-1)。

NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];

if (urlResponse.expectedContentLength != NSURLResponseUnknownLength) {
    [req setRange:NSMakeRange(progress, NSNotFound)];
}
于 2012-07-16T14:53:49.050 に答える