itok's Lab

昔の開発ネタを記録として残してます

iPhotoプラグインを作る「フォト蔵編」-8 : CFNetwork -1

以前、お話していた NSURLRequest と NSURLConnection でサーバとの通信をするというやつですが、いろいろやっているとどうも POST がうまくいきません。必ずうまくいかないというわけではなくて、うまくいかないことがある、というどうにも面倒な感じで。

で、例によって、いろいろとネットを徘徊したところ、なにやら、他のところでもこの件で困っている人がいるようです。

結論としては、NS ではなく CF を使いましょうってことで。手元では CFHTTPMessageRef や CFReadStream を使っての通信(GET も POST も)をなんの問題もなく出来てしまってます。なんか、釈然としないところもあるような感じですが、まあよしということにしましょう。

参考にしたのは、以下のあたり。

この件についてもおいおい詳しく書いていくつもりです。

iPhotoプラグインを作る「フォト蔵編」-7 : ExportMgr メモ -1

iPhotoヘのアクセスを担うExportMgrのAPIメモ

書き出し対象の画像数
-(unsigned int) imageCount;
画像ファイルのパス(NSString)
-(id) imagePathAtIndex:(unsigned int)fp8;
画像ファイルのタイトル(NSString)
-(id) imageTitleAtIndex:(unsigned int)fp8;
画像ファイルのコメント(NSString)
-(id) imageCommentsAtIndex:(unsigned int)fp8;
画像ファイルのキーワード(NSArray)
-(id) imageKeywordsAtIndex:(unsigned int)fp8;

とりあえず、このあたりがあれば、必要な情報は手に入れられそうです。

Labの過去記事を復活させました

サーバ故障からようやく本格的に立ち直ってきました。最後のバックアップがとれていた2007年5月以前のブログデータをそのままの形で復活させました。一応、mod_rewriteも使って、以前のURLでもアクセスできるはず。

2007年5月以降のデータはというと、そんなに更新頻度が高くなかったおかげでもあって幸いにもキャッシュに残っていたので、こちらの新規ブログにすでに掲載済みです。

昔のもいずれ、こちらに統合しようかと思っているんですが、まあ、前のURLを補完したいっていうのもありますし、難しいところですね。

iPhotoプラグインを作る「フォト蔵編」-6 : フォト蔵APIヘのアクセス-2 : 写真のアップロード

前回までの NSURLRequest の話を利用して、実際にフォト蔵へ写真をアップロードしてみましょう。使用するAPIphoto_add です。

#define ASCII_DATA(str) [(NSString*)(str) dataUsingEncoding:NSASCIIStringEncoding]

{
    NSURL* apiUrl = [NSURL URLWithString:@"http://api.photozou.jp/rest/photo_add"];
    NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:apiUrl];
    // Base64エンコーディング
    NSData* baseData = ASCII_DATA(([NSString stringWithFormat:@"%@:%@", username, password]));
    NSString* baseStr = [baseData stringEncodedWithBase64];
    // Basic認証ヘッダ追加
    [req setValue:[NSString stringWithFormat:@"Basic %@", baseStr] forHTTPHeaderField:@"Authorization"];

    // 画像パス
    NSURL* imgUrl = [NSURL fileURLWithPath:imgPath];
    // マルチパート生成 (前回のエントリ参照)
    NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
        album_id,   @"album_id",
        imgUrl,       @"photo",
        nil];
    NSData* formData = [self generateFormData:params boundary:@"___boundary___"];
    [req addValue: @"multipart/form-data; boundary=___boundary___" forHTTPHeaderField: @"Content-Type"];
    [req setHTTPBody:formData];

    // POSTで送信
    [req setHTTPMethod:@"POST"];
    NSURLResponse* res;
    NSData* response = [NSURLConnection sendSynchronousRequest:req returningResponse:&res error:nil];
}

これで、各種パラメータを適切に設定できていれば、指定のアルバムに指定の画像ファイルを追加することが出来ます。ここまでくれば、まあ、あともう少しですかね。多分。

iPhotoプラグインを作る「フォト蔵編」-5 : NSURLRequest -2 : データ転送

次は、NSURLRequestでデータを転送する話。特にPOSTの場合。

これもよくわからなかったので、いろいろとネットを徘徊した結果、こちらを参考にしました。

要するに、マルチパートのメッセージを自前でこしらえる必要があるっていうだけのことなんですが、まあ、一から考えるのは手間がかかるので、既存のサンプルを参考にさせてもらったといいますか。

単純にContent-Typeをimage/jpeg固定だとして、上記ソースにちょっとだけ手を加えたものが、以下の通りです。

#define ASCII_DATA(str) [(NSString*)(str) dataUsingEncoding:NSASCIIStringEncoding]

// マルチパート生成
-(NSData*) generateFormData:(NSDictionary*)dict boundary:(NSString*)boundary
{
    NSMutableData* result = [[NSMutableData alloc] init];
    id key;
    NSEnumerator* enume = [dict keyEnumerator];

    while (key = [enume nextObject]) {
        id value = [dict valueForKey:key];
        [result appendData:ASCII_DATA(([NSString stringWithFormat:@"--%@\\n", boundary]))];
        if ([value isKindOfClass:[NSString class]]) {
            // 文字列
            // 単純に追加
            [result appendData:ASCII_DATA(([NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"\\n\\n", key]))];
            [result appendData:ASCII_DATA(([NSString stringWithFormat:@"%@",value]))];
        } else if ([value class] == [NSURL class] && [value isFileURL]) {
            // データパス
            // データパス文字列を追加
            [result appendData:ASCII_DATA(([NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"\\n", key, [[value path] lastPathComponent]]))];
            // Content-Type
            [result appendData:ASCII_DATA(([NSString stringWithString:@"Content-Type: image/jpeg\\n\\n"]))];
            // データの追加
            [result appendData:[NSData dataWithContentsOfFile:[value path]]];
        }
        [result appendData:ASCII_DATA(([NSString stringWithString:@"\\n"]))];
    }
    [result appendData:ASCII_DATA(([NSString stringWithFormat:@"--%@--\\n", boundary]))];
    return [result autorelease];
}

// 呼び出し側
{
    NSData* formData = [self generateFormData:params boundary:@"___boundary___"];
    [req addValue: @"multipart/form-data; boundary=___boundary___" forHTTPHeaderField: @"Content-Type"];
    [req setHTTPBody:formData];
}

とりあえず、これでファイルのPOSTはうまくいきます。

iPhotoプラグインを作る「フォト蔵編」-4 : NSURLRequest -1 : Basic認証

ちょっとの間、フォト蔵、というよりCocoaによる通信のお話。

CocoaでHTTP等の通信を行う場合、NSURLRequestを使うのが一般的でしょう。ここでは、NSURLRequestの一般的な使い方、というより、ちょっとだけ突っ込んだところを見てみます。

で、Basic認証です。Basic認証についての概要は毎度おなじみWikipediaにたよるとして、要するに、username:passwordの組み合わせをBase64エンコードして、HTTPヘッダにのっける(Authorization: Basic xxxxxx)ということです。やることは二つですね。順番に見ていきましょう。

Base64エンコード

これは、ネットで公開されているサンプルソースをお借りすることにしましょう。(感謝感謝>グースさん)

NSDataのカテゴリとして実装されているので、非常に便利な感じです。ここではNSDataをNSStringとして出力する形となっていますので、そのまま使うとすれば、こういうふうになります。

NSData* data = [[NSString stringWithFormat:@"%@:%@", username, password]
    dataUsingEncoding:NSASCIIStringEncoding];
NSString* base64Str = [data stringEncodedWithBase64];

簡単ですね。

HTTPヘッダへの追加

NSURLRequestのインスタンスにヘッダ情報を追加するわけですが、NSURLRequestはimmutableなのでNSMutableURLRequestを使用します。

// NSMutableURLRequest
-(void) setValue:(NSString*)value forHTTPHeaderField:(NSString*)field;

前述のからの流れとしてはこういう感じになります。

NSMutableURLRequest* req = [NSMutableURLRequest requestWithURL:url];
[req setValue:[NSString stringWithFormat:@"Basic %@", base64Str]
    forHTTPHeaderField:@"Authorization"];

で、このreqを用いて、POSTなりGETをしてあげればOKです。

iPhotoプラグインを作る「フォト蔵編」-3 : フォト蔵APIヘのアクセス-1 : まずはcurlで

さて、プラグイン側のひな形も出来たところで、次はフォト蔵へのアクセスを考えましょう。

多くのWebサービスがそうであるように、フォト蔵も外部からアクセスするためのAPIを公開しています。

このAPIRESTと呼ばれるタイプでして、ものすごく簡単にいうと、指定のURLに対してGETやらPOSTでアクセスすると結果としてXMLが返ってくる、というものです。

あとは、上記サイトに書いてある通りのAPI仕様なのですが、まあ、ここで気にすべきは

  • 認証はBasic認証
  • ファイル送信時にはContent-Typeを指定(全部小文字)

くらいのものでしょう。

早速、試しにcurlを使って nop にアクセスしてみるとこんな感じ。

# curl -u mail@example.com:password http://api.photozou.jp/rest/nop


次はこれを Cocoa から動かします。