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
を使用します。
- アップロードは、以下の手順で行います。
- CONFIGファイルに、
UPLOAD=1
を書き込む - FlashAirを再起動する
- FlashAirに無線LAN接続する
WRITEPROTECT
コマンドで、SDメモリカードホスト機器からの書き込みを禁止するUPDIR
コマンドで、アップロード先フォルダを設定するFTIME
コマンドで、ファイルの作成日時を設定する- upload.cgiにファイルをPOSTし、ファイルをアップロードする
- CONFIGファイルに、
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 ボタンをタップします。
2013/03/03 をタップします。
2013/03/03 の操作画面が表示されました。
メモ&タイトルを入力し、Done ボタンをタップして保存します。
Back ボタン をタップし前画面に戻ると、保存した内容が表示されました。
サンプルコード
ios_tutorial_07.zip (62KB)
このサイトのサンプルコードは二条項BSDライセンスで提供されています。