環境 | iOS SDK 4.0 |
NSURLConnectionを使ってデータをダウンロードするとき、 ダウンロードデータを格納するNSMutableDataの容量をダウンロードデータのサイズと合わせておこうと思って NSURLConnectionのdelegateを担うクラスに以下のようなコードを書きました。
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { long long length = response.expectedContentLength; receivedData = [[NSMutableData alloc] initWithCapacity:length]; }
そうしたところ、NSMutableDataのinitWithCapacity:が例外で落ちました。
response.expectedContentLengthに-1が入っているようです。
HTTP Responseからサイズを取得できないときはそうなるようですが、
今時サイズを取得できないようなResponseを返すサーバがあるのかなと思って調べてみました。
HTTPのRequestとResponseをキャプチャしたかったので、横取り丸 1.98と
InetSpy 1.1を使って、HTTPをモニタできる環境を整え、
iPodのプロキシ設定を変更してRequestとResponseをキャプチャしてみました。
なお、iPhone Simulatorでプロキシ設定を変更する方法は分からなかったので、実機でしか確認できませんでした。
結果、Responseに以下のEntityがあることが分かりました。
Content-Encoding: gzip
そりゃ圧縮されていたらサイズ分かりませんね・・・。 今時のサーバでは圧縮はよくある話なのでresponse.expectedContentLengthに-1が入るのは納得しました。
connection:didReceiveResponse:は以下のように書き直しました。
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { const int MAX_INIT_SIZE = 100 * 1024 * 1024; long long length = response.expectedContentLength; if (length < 0 || length > MAX_INIT_SIZE) { length = MAX_INIT_SIZE; } receivedData = [[NSMutableData alloc] initWithCapacity:length]; }
response.expectedContentLengthに-1が入ってきた場合にも対応し、
さらに上の方も100KBで制限をかけることにしました。いきなり大きなサイズにするのも気がひけたからです。
これでデータのダウンロードもスムーズになるのではないかと思います。(本当になるかどうかは確認できませんでしたが)
以下はおまけですが、InetSpyでは、gzipで圧縮されたファイルしか取れないため、展開後のデータを確認できません。
確認するためにPerlスクリプトを作成しましたので、参考までにコードを置いておきます。
# 参考コード: http://www.tohoho-web.com/perl/encode.htm use strict; use Compress::Zlib; die "Usage: gzip [IN_FILE] [OUT_FILE]\n" if ($#ARGV < 1); my $infile = $ARGV[0]; my $outfile = $ARGV[1]; my $indata; my $outdata; open(IN, $infile) || die "Can't open $infile.\n"; binmode(IN); read(IN, $indata, (lstat(IN))[7]) || die "Can't read $infile\n"; close(IN); open(OUT, "> $outfile") || die "Can't open $outfile.\n"; binmode(OUT); print OUT Compress::Zlib::memGunzip($indata); close(OUT);