FlashAirへのアップロード

このチュートリアルでは、CGI経由でFlashAirにファイルをアップロード(書き込み)する方法について学びます。 使用するCGIはupload.cgi です。 このチュートリアルは別項のiOS Tutorial 4: サムネイルの表示 に基づいています。


概要

FlashAirに保存されている画像を、日付別にタイトル&メモをつけて管理できるようにします。

注意: 本機能を誤って使用すると、FlashAirのファイルシステムを破壊してデータを失う恐れがあります。PCなどのSDメモリカードホスト機器はSDメモリカードの内容(FAT)をキャッシュしている場合がありますが、本CGIで行った変更をSDメモリカードホスト機器が認識する方法がありません。そのため、本CGIとSDメモリカードホスト機器から同時に変更を行うとFAT不整合が起こる可能性があります。 CGIからの書き込み後は必ずカードをいったん抜いて再挿入するなどしてSDメモリカードホスト機器に再認識させてください。


画面レイアウトの作成

レイアウトはこのように作ります。

(レイアウト)

iOS Tutorial 4: サムネイルの表示に、画面3:日付一覧、画面4:操作画面を追加します。

画面1:コンテンツリストに Date List ボタンを追加します。

Date List ボタンをタップされると画面3:日付一覧を表示します。

日付一覧の日付がタップされると、その日付の画面4:操作画面を表示します。


コードの作成

画面1:コンテンツリストの作成

iOS Tutorial 4: サムネイルの表示の状態に、Date List ボタンをタップした際の処理を追加します。 また、ここでは画像ファイルのみを対象とするので、絞り込み条件の追加等も行います。 詳しい内容についてはサンプルコードを確認してください。

画面2:画像表示画面の作成

画像表示画面 はiOS Tutorial 3: コンテンツのダウンロードと同じです。 実装の詳細についてはこのチュートリアルを参照してください。

画面3:日付一覧の作成

コンテンツリストで選択していたフォルダ内にある画像ファイルの、保存日付をタイトルとともに一覧で表示するようにします。 タイトルは、1行目にタイトル、2行目にメモの情報を書き込んだテキストファイルから取得します。 ファイルはYYYYMMDD.txt という名前(YYYYMMDDは日付が入ります)で、保存画像と同じフォルダに、画面4:操作画面から保存します(保存部分については後述します)。

まずは画像の日付を取得します。

FSDatelistViewController.m (1)
    NSMutableDictionary *ret = nil;

    // Find unique dates of files
    NSMutableSet *dates = [NSMutableSet set];
    for (NSString *fileInfo in self.files) {
        NSArray *tokens = [fileInfo componentsSeparatedByString:@","];

        // Skip if it is the signature line or an empty line or not image file.
        NSString *dir = [tokens objectAtIndex:0];
        NSString *filename = [tokens objectAtIndex:1];
        NSString *ext = [[filename pathExtension] lowercaseString];
        if([dir isEqualToString:@""] || [dir isEqualToString:@"WLANSD_FILELIST\r"] ||
           !([ext isEqualToString:@"jpg"] || [ext isEqualToString:@"jpeg"] ||
            [ext isEqualToString:@"png"] || [ext isEqualToString:@"jpe"])){
            continue;
        }

        // Add date.
        [dates addObject:[tokens objectAtIndex:4]];
    }

次に、各日付のタイトルを取得します。

FSDatelistViewController.m (2)
    // Load existing memo.
    ret = [NSMutableDictionary dictionary];
    NSDateFormatter *toFormatter = [[NSDateFormatter alloc] init];
    [toFormatter setDateFormat:@"yyyyMMdd"];        //for filename
    NSDateFormatter *tostrFormatter = [[NSDateFormatter alloc] init];
    [tostrFormatter setDateFormat:@"yyyy/MM/dd"];   //for show display
    for (NSString *date in dates) {
        NSError *error = nil;

        // Convert date number to string.
        NSString *sDate= [toFormatter stringFromDate:[self dateAsDate:date]];
        NSString *strDate= [tostrFormatter stringFromDate:[self dateAsDate:date]];

        // Make up URL to the memo file.
        NSURL *url=[NSURL URLWithString:[NSString stringWithFormat:@"http://flashair%@/%@.txt",
                                                                                self.path, sDate]];

        // Get memo contents from the FlashAir.
        NSString *title = @"";
        NSString *body = @"";
        NSString *memoData =[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding 
                                                                                     error:&error];
        if ( ![error.domain isEqualToString:NSCocoaErrorDomain] && [memoData rangeOfString:@"DOCTYPE" 
                                             options:NSCaseInsensitiveSearch].location == NSNotFound) {
            // Success to get existing memo.
            NSArray *data = [memoData componentsSeparatedByString:@"\n"];
            title = [data objectAtIndex:0];
            body = [data objectAtIndex:1];
        }

        // Add this memo to the list.
        NSDictionary *memo = [NSDictionary dictionaryWithObjectsAndKeys:
                              title, @"title",
                              body, @"body",
                              strDate, @"date",
                              nil];
       [ret setObject:memo forKey:sDate];
    }
  • 15-29行目
    日付をもとにパスを作成し、タイトルとメモが保存されたファイルをダウンロードします。

画面4:操作画面の作成

日付一覧で選択した日付のタイトル、メモの編集と保存を行います。

アップロード

タイトル&メモを保存するためには、upload.cgiを使用します。

  • アップロードは、以下の手順で行います。
    1. CONFIGファイルに、UPLOAD=1を書き込む
    2. FlashAirを再起動する
    3. FlashAirに無線LAN接続する
    4. WRITEPROTECTコマンドで、SDメモリカードホスト機器からの書き込みを禁止する
    5. UPDIRコマンドで、アップロード先フォルダを設定する
    6. FTIMEコマンドで、ファイルの作成日時を設定する
    7. upload.cgiにファイルをPOSTし、ファイルをアップロードする

3.までは完了しているものとして解説していきますので、事前に手動で設定をしておきましょう。 設定は、一度だけで構いません。

では、手順の4-6を行います。

FSMemoEditViewController.m (1)
    // Set Write-Protect and upload directory and System-Time
    // Make System-Time
    NSDate *systemdate = [NSDate date];
    NSCalendar *calendar = [NSCalendar currentCalendar];
    NSDateComponents *dateCompnents;
    dateCompnents =[calendar components:NSYearCalendarUnit
                                        | NSMonthCalendarUnit
                                        | NSDayCalendarUnit
                                        | NSHourCalendarUnit
                                        | NSMinuteCalendarUnit
                                        | NSSecondCalendarUnit fromDate:systemdate];

    NSInteger year =([dateCompnents year]-1980) << 9;
    NSInteger month = ([dateCompnents month]) << 5;
    NSInteger day = [dateCompnents day];
    NSInteger hour = [dateCompnents hour] << 11;
    NSInteger minute = [dateCompnents minute]<< 5;
    NSInteger second = floor([dateCompnents second]/2);

    NSString *datePart = [@"0x" stringByAppendingString:[NSString stringWithFormat:@"%x%x" ,
                                                        year+month+day,hour+minute+second]];

    // Make Filename
    NSString *filename=[[self.date stringByReplacingOccurrencesOfString:@"/" withString:@""]
                                                         stringByAppendingString :@".txt"];

    // Make url
    NSString *urlStr = @"http://flashair/upload.cgi";
    urlStr = [urlStr stringByAppendingString:@"?WRITEPROTECT=ON&UPDIR="];
    urlStr = [urlStr stringByAppendingString:self.path];
    urlStr = [urlStr stringByAppendingString:@"&FTIME="];
    urlStr = [urlStr stringByAppendingString:datePart];
    NSURL *url = [NSURL URLWithString:urlStr];
    // Run cgi
    NSError *error;
    NSString *rtnStr =[NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
    if ([error.domain isEqualToString:NSCocoaErrorDomain]){
        NSLog(@"upload.cgi %@\n",error);
        return;
    }else{
        if(![rtnStr isEqualToString:@"SUCCESS"]){
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:self.title
                                                        message:@"upload.cgi:setup failed" delegate:nil
                                                  cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
            [alert show];
            return;
        }
    }
  • 3-21行目
    現在の日付からファイルの作成日時を指定するための日付文字列を生成しています。
    日付、時刻をそれぞれ16ビットの16進数として作成し、合わせて32ビットの1つの値として渡しています。

次に、手順の7を行います。

FSMemoEditViewController.m (2)
    // File upload
    // Make Data
    self.memoTitle = self.textFieldTitle.text;
    self.memoBody = self.textViewMemo.text;
    NSString *memoData = [[self.memoTitle stringByAppendingString:@"\n"] 
                                                                stringByAppendingString:self.memoBody];
    NSData *textData=[memoData dataUsingEncoding:NSUTF8StringEncoding ];

    //url
    url=[NSURL URLWithString:@"http://flashair/upload.cgi"];

    //boundary
    CFUUIDRef uuid = CFUUIDCreate(nil);
    CFStringRef uuidString = CFUUIDCreateString(nil, uuid);
    CFRelease(uuid);
    NSString *boundary = [NSString stringWithFormat:@"flashair-%@",uuidString];

    //header
    NSString *header = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];

    //body
    NSMutableData *body=[NSMutableData data];
    [body appendData:[[NSString stringWithFormat:@"--%@\r\n",boundary] 
                                                            dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"file\";
                             filename=\"%@\"\r\n",filename] dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:[[NSString stringWithFormat:@"Content-Type: text/plain\r\n\r\n"]
                                                            dataUsingEncoding:NSUTF8StringEncoding]];
    [body appendData:textData];
    [body appendData:[[NSString stringWithFormat:@"\r\n--%@--\r\n", boundary]
                                                            dataUsingEncoding:NSUTF8StringEncoding]];


    //Request
    NSMutableURLRequest *request =[NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];
    [request addValue:header forHTTPHeaderField:@"Content-Type"];
    [request setHTTPBody:body];

    NSURLResponse *response;

    NSData *result = [NSURLConnection sendSynchronousRequest:request
                                           returningResponse:&response
                                                       error:&error];
    rtnStr=[[NSString alloc] initWithData:result encoding:NSUTF8StringEncoding];

    if ([error.domain isEqualToString:NSCocoaErrorDomain]){
        NSLog(@"upload.cgi %@\n",error);
        return;
    }else{
        if([rtnStr rangeOfString:@"Success"].location==NSNotFound){     //v2.0
            UIAlertView *alert = [[UIAlertView alloc] initWithTitle:self.title
                                            message:@"upload.cgi: POST failed" delegate:nil
                                                cancelButtonTitle:nil otherButtonTitles:@"OK", nil];
            [alert show];
            return;
        }
    }
  • 19行目
    multipart/form-data形式を指定しています。

実行結果

メモ&タイトルを保存してみます。

Date List ボタンをタップします。

(This image shows the result)

2013/03/03 をタップします。

(This image shows the result)

2013/03/03 の操作画面が表示されました。

(This image shows the result)

メモ&タイトルを入力し、Done ボタンをタップして保存します。

(This image shows the result)

Back ボタン をタップし前画面に戻ると、保存した内容が表示されました。

(This image shows the result)


サンプルコード

ios_tutorial_07.zip (62KB)

このサイトのサンプルコードは二条項BSDライセンスで提供されています。