問題は次でした。
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)];
}