Objective-Cでファイルのアップロード処理を実装


ブログなんかでよくある、画像と動画のアップロードをアプリ側から行う事案が発生したので、調べてみた。


まずは、アプリ側とサーバ側との通信は、こちらのライブラリを使用させてもらった。
シンプルで簡単に HTTP 通信が出来るライブラリを公開しました

Xcodeにインポートして、ライブラリ内の「Multipart POST」を使用する。
アプリというかiPhoneなりiPodなりで撮った写真をアップロードしたいので、以下のようなリクエストをサーバ側のファイルに対して投げる。

NSURL *URL = [NSURL URLWithString:@"http://.../hoge.php"];
R9HTTPRequest *request = [[R9HTTPRequest alloc] initWithURL:URL];
[request setHTTPMethod:@"POST"];
[request addBody:@"upload" forKey:@"query_type"];
[request setData:upData withFileName:strFileName andContentType:strContentsType forKey:strKey];

// リクエスト結果
[request setCompletionHandler:^(NSHTTPURLResponse *responseHeader, NSString *responseString){
    //NSLog(@"%@", responseString);
}];

// 画像アップロードの進捗状況を確認(1 = アップロード完了)
[request setUploadProgressHandler:^(float newProgress){
    NSLog(@"%g", newProgress);
    if (newProgress == 1) {
        NSLog(@"%@", "アップロードが完了しました!");
    }
}];

[request startRequest];

こんな感じ。setDataメソッドに対して画像が格納された、NSDataオブジェクトとアップロードしたい画像ファイルのContentTypeを指定するだけ。ラクチン!
今回はiOSデバイスで撮った画像と動画をアップロードしたかったので、ContentTypeはそれぞれ「image/png」と「video/quicktime」になります。
アップロードするには、サーバ側の設定なんかも必要です。Apacheの設定ですね。
めんどくさいので割愛します。

ここまでで単体ファイルのアップロードは実装できた。
実際のアプリでは、画像が最大3つ・動画が1つ同時にアップロード出来るの仕様(の予定)。
んで、それらのアップロードが全て完了したときにポップアップなりで知らせたかった。
普通にファイルを順序良くアップロードすると、完了前にポップアップが出ちゃう。
setUploadProgressHandlerでファイルのアップロードが完了した時に、次のファイルをアップロードするというネスト(笑)な作りにも出来たけど、これはあんまりだ…。

ってことで、「NSRunLoop」を使って一つのファイルがアップロードされるのを待って、次の処理を実行させるようにした。
(並列処理もできると思うけど、今回はこれで勘弁)

NSData *pngData = [[NSData alloc] initWithData:UIImagePNGRepresentation(img1)];
[self upData:pngData strFileName:@"photo1.png" strContentsType:@"image/png" strKey:@"upfile"];
//アラートでボタンを押すまで動作を中断する(待つ)
alertFinished = NO;
while (alertFinished == NO) {
    [[NSRunLoop currentRunLoop]
     runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5f]]; //0.5秒
}

コードはこんな感じ。アップロード処理を実行させたあとに、while文内でNSRunLoopを実行。
これで、alertFinishedフラグがONになるまでは次の処理をさせないように出来た。
alertFinishedをONにするタイミングはもちろん、アップロードが完了した時。
こんな感じでフラグをONにする。

// 画像アップロードの進捗状況を確認(1 = アップロード完了)
[request setUploadProgressHandler:^(float newProgress){
    NSLog(@"%g", newProgress);
    if (newProgress == 1) {
        NSLog(@"%@", "アップロードが完了しました!");
        alertFinished = YES;
    }
}];

あとは、この処理を繰り返せば一つ一つ順序良くアップロードができる、と思う。
まぁアップロードに失敗した時の例外処理なんかも必要だと思うので、一定時間待ったらループを抜ける処理とかも後から追加していきたいです。

この記事を書いた人

Hoge Huga