前回の記事ではダウンロードをひと通り取り扱った。
今回はデータをアップロードする処理を実装してみることにしよう。
前回記事↓
[iOS] NSURLSessionを使って通信を行う
いきなりマルチパートデータをPOSTするのではなく、簡単なところから少しずつステップアップしていこう。
なお、今回の記事を作成するにあたり、こちらを大いに参考にさせていただいた。
Send POST request using NSURLSession - stackoverflow
テキストデータをPOSTする
まずは一番簡単な処理、テキストのみのPOSTだ。
今回はデータをアップロードする処理を実装してみることにしよう。
前回記事↓
[iOS] NSURLSessionを使って通信を行う
いきなりマルチパートデータをPOSTするのではなく、簡単なところから少しずつステップアップしていこう。
なお、今回の記事を作成するにあたり、こちらを大いに参考にさせていただいた。
Send POST request using NSURLSession - stackoverflow
テキストデータをPOSTする
まずは一番簡単な処理、テキストのみのPOSTだ。
このように実装する。
基本的な流れはダウンロードの時とほとんど変わらない。
ほんの少しだけ処理を付け加えてやればいいだけだ。
基本的な流れはダウンロードの時とほとんど変わらない。
ほんの少しだけ処理を付け加えてやればいいだけだ。
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
これはHTTPリクエストの設定をするためのものだ。
具体的な設定方法については後で出てくる。
MethodとBodyを代入してやれば設定は完了だ。
後はダウンロードの時と同じ流れでタスクを作成し、実行させてやればいい。
テキストと画像をマルチパートデータにしてPOSTする
いよいよ今回の本題だ。
テキストと画像をマルチパートデータにしてPOSTしてみよう。
このように実装する。
こんな長い処理になってしまってうんざりしてしまうかもしれない。
しかし、データの組み立て部分以外はテキストのみの場合とほとんど同じで、違うのはこの部分だ。
次はいよいよ今回の肝、マルチパートデータの組み立てだ。
具体的な設定方法については後で出てくる。
NSData* data = [@"param1=りんご¶m2=みかん" dataUsingEncoding:NSUTF8StringEncoding];
ここは特に重要な箇所だ。
POSTするデータはテキストのみにしろ、画像を加えるにしろ、全てNSDataにすることになる。
今回はテキストのみなので、作成は実にシンプルだ。
"りんご"と"みかん"という2つのテキストをPOSTする。
POSTするデータはテキストのみにしろ、画像を加えるにしろ、全てNSDataにすることになる。
今回はテキストのみなので、作成は実にシンプルだ。
"りんご"と"みかん"という2つのテキストをPOSTする。
request.HTTPMethod = @"POST"; request.HTTPBody = data;ここで先ほど作成したリクエストの設定を行っている。
MethodとBodyを代入してやれば設定は完了だ。
後はダウンロードの時と同じ流れでタスクを作成し、実行させてやればいい。
テキストと画像をマルチパートデータにしてPOSTする
いよいよ今回の本題だ。
テキストと画像をマルチパートデータにしてPOSTしてみよう。
このように実装する。
こんな長い処理になってしまってうんざりしてしまうかもしれない。
しかし、データの組み立て部分以外はテキストのみの場合とほとんど同じで、違うのはこの部分だ。
NSString* boundary = @"MyBoundaryString"; NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; config.HTTPAdditionalHeaders = @{ @"Content-Type" : [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] };
リクエストヘッダにContent-Typeを追加し、マルチパートであることを明示する。
boundaryというのはマルチパートでデータの境界を表すためのもので、ここでは単語として読めるものにしてあるが、実際はデータと被らないようなランダムな文字列にした方が良いだろう。次はいよいよ今回の肝、マルチパートデータの組み立てだ。
NSMutableData* data = [NSMutableData data]; // テキスト部分の設定 [data appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"name=\"%@\"\r\n\r\n", @"param1"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"%@\r\n", @"すいか"] dataUsingEncoding:NSUTF8StringEncoding]]; // 画像の設定 [data appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"name=\"%@\";", @"upload_file"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"filename=\"%@\"\r\n", @"sample1.jpg"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"Content-Type: image/jpeg\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:imageData]; [data appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; // 最後にバウンダリを付ける [data appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
一見するとかなり複雑そうに見えるが、ある程度の規則性があるのはわかる。
この部分の説明を行うととても長くなってしまうので、おまじない的にこうすれば良いくらいに捉えてても構わない。
どうしても詳細を知りたい場合はRFCを読んでみるなりしてほしい。
なお、一つだけ気をつけなければならない点がある。
なお、NSURLSessionではメモリリークが起きる場合があるので気をつけたほうが良い。
詳しくはこちらの記事にまとめた。
[iOS] NSURLSessionのメモリリークに気をつける
この部分の説明を行うととても長くなってしまうので、おまじない的にこうすれば良いくらいに捉えてても構わない。
どうしても詳細を知りたい場合はRFCを読んでみるなりしてほしい。
なお、一つだけ気をつけなければならない点がある。
Objective-Cでは(Xcodeでは?)'\'は'¥'とは異なるということだ。
'¥'でダブルクォーテーションをエスケープしようとするとXcode上でエラーが出るから気づくとは思うが、ここはハマりやすいところなので注意しよう。
任意の数のテキストと画像をPOSTするメソッドを作成する
最後に総まとめとして、任意の数のテキストと画像をPOSTするメソッドを作成する。
NSDictionaryにデータを設定し、引数として渡すと、それらをまとめてPOSTするというものだ。
このように実装する。
- (void)postMultiDataWithTextDictionary:(NSDictionary*)textDictionary imageDictionary:(NSDictionary*)imageDictionary url:(NSURL*)url delegate:(id<NSURLSessionDelegate>)delegate { NSURLSessionConfiguration* config = [NSURLSessionConfiguration defaultSessionConfiguration]; NSString* boundary = @"MyBoundaryString"; config.HTTPAdditionalHeaders = @{ @"Content-Type" : [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] }; NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url]; NSURLSession* session = [NSURLSession sessionWithConfiguration:config delegate:delegate delegateQueue:[NSOperationQueue mainQueue]]; // postデータの作成 NSMutableData* data = [NSMutableData data]; // テキスト部分の設定 for (id key in [textDictionary keyEnumerator]) { NSString* value = [textDictionary valueForKey:key]; [data appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"name=\"%@\"\r\n\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"%@\r\n", value] dataUsingEncoding:NSUTF8StringEncoding]]; } // 画像の設定 for (int i = 0; i < [imageDictionary count]; i++) { NSString* key = [[imageDictionary allKeys] objectAtIndex:i]; NSData* value = [imageDictionary valueForKey:key]; NSString* name = [NSString stringWithFormat:@"upload_file%d", i]; [data appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data;"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"name=\"%@\";", name] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"filename=\"%@\"\r\n", key] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:[[NSString stringWithFormat:@"Content-Type: image/jpeg\r\n\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; [data appendData:value]; [data appendData:[[NSString stringWithFormat:@"\r\n"] dataUsingEncoding:NSUTF8StringEncoding]]; } // 最後にバウンダリを付ける [data appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]]; request.HTTPMethod = @"POST"; request.HTTPBody = data; NSURLSessionDataTask* task = [session dataTaskWithRequest:request]; [task resume]; }これを使用するにはこのように呼び出す。
なお、NSURLSessionではメモリリークが起きる場合があるので気をつけたほうが良い。
詳しくはこちらの記事にまとめた。
[iOS] NSURLSessionのメモリリークに気をつける
コメント
コメント一覧 (3)
最近プログラミングを始めた者です。
Xcodeとサーバ間の通信について調べていたところ、こちらのブログにたどり着きました。
大変参考にさせていただいています。
テキストのみの送信まではできたので、次にテキストと画像のマルチパートデータを送信しようとしたところ、何分待っても送信が完了しません。。。
以下のようなphpで受け取ったデータをデータベースに格納することを考えているのですが、どこかにおかしな点があるのでしょうか?
また、画像を格納するフィールドの型は何にすれば良いのでしょうか?
初歩的でなおかつ記事とは直接関係のない部分で大変恐縮ですが、ご教授いただけるとありがたいです。
どうかよろしくお願い致します。
//情報受信
$text = $_POST["param1"];
$image = $_POST["upload_file"];
//処理
$stmt = $dbh->prepare("insert into PostTest (field1,field2) values (?,?)");
$stmt->execute(array($text,$image));
PHPで受ける場合、画像ファイル情報は$_POSTではなく$_FILESに格納されています。
以下のページで詳細に解説されているので、ご参照ください。
ファイルのアップロード | PHP Labo
http://www.php-labo.net/tutorial/php/upload.html
調べてみると他にも解説のページがたくさん出てきたので勉強してみたいと思います。