[Parse][iOS] PFFileの使い方

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

まえおき

前回の記事で、「PFObject のサイズ上限は 128KB を超えちゃダメなので、ファイルを保存する場合は PFFile を使用しましょう」って話をしました。

今回はこの PFFile の使い方について、公式ドキュメントに習って解説していきたいと思います。

PFFileとは?

Parse でファイルを保存する場合、PFObject にバイナリとして保存する方法がありますが、いかんせん PFObjeftは1レコードあたり 128KB という上限があるため、大きめのファイルを保存するには適しておりません。そこで PFFile の登場です。PFFile では、画像、ドキュメント、ビデオ、音楽ファイルなど様々なファイルを保存することが可能です。尚、1ファイルあたり最大 10MB までの上限があります。

PFFileの使い方

PFFile の使い方は非常に簡単で、以下のように記述するだけで PFFile インスタンスを生成できます。

NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:@"resume.txt" data:data];

参考:iOS/OS X Developer Guide | Parse

上記のコードは、「Working at Parse is great!」という文字列から生成した NSData インスタンスから PFFile インスタンスを生成しています。ここで、PFFile のコンビニエンスコンストラクタでファイル名として resume.txt を指定していることに注目してください。PFFile では、ファイル名を指定に関して、以下の2つの注意点があります。

  1. Parse では各アップロードに対して一意の識別子を付与するため、ファイル名の衝突を心配する必要はありません。
  2. Parse ではファイル名に含まれる拡張子をもとにファイルの種類を把握するため、ファイル名には拡張子を含めましょう。

PFFileを保存する

生成した PFFile インスタンスは、PFObject に関連付けて保存することで、ファイルをクラウド上に保存することができます。

// PFFileインスタンスを生成する
NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:@"resume.txt" data:data];

// PFObjectインスタンスにPFFileインスタンスを関連付けて保存する
PFObject *jobApplication = [PFObject objectWithClassName:@"JobApplication"]
jobApplication[@"applicantName"] = @"Joe Smith";
jobApplication[@"applicantResumeFile"] = file;
[jobApplication saveInBackground];

保存処理を実行すると、Data Browserで以下のように表示されるはずです。

parse-ios-pffile-2

基本的にはこれでクラウド上にファイルを保存することができますが、PFFile自体にもPFObjectと同様、save〜 系メソッドが用意されています。

NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:@"resume.txt" data:data];
[file saveInBackground];

PFFile の save〜 系メソッドは以下のものが用意されています。

- (BOOL)save
同期的に保存処理を行う。
- (BOOL)save:(NSError **)error
同期的に保存処理を行う。エラー付き。
- (void)saveInBackground
非同期で保存処理を行う。
- (void)saveInBackgroundWithBlock:(PFBooleanResultBlock)block
非同期で保存処理を行う。
保存処理が終了したら引数で指定したBlocksが実行される。
- (void)saveInBackgroundWithBlock:(PFBooleanResultBlock)block progressBlock:(PFProgressBlock)progressBlock
非同期で保存処理を行う。
保存処理が終了したら引数で指定したBlocksが実行される。
保存処理中は定期的にprogressBlockが呼ばれ進捗を確認できる。
- (void)saveInBackgroundWithTarget:(id)target selector:(SEL)selector
非同期で保存処理を行う。
保存処理が終了したらtargetで指定したインスタンスのselectorで指定したメソッドが実行される。

ここで注意しなければならないのが、PFFile の save〜 系メソッドを用いて保存したあとに結局PFObject に関連付けなければなりません。

NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:@"resume.txt" data:data];
[file saveInBackground];

PFObject *jobApplication = [PFObject objectWithClassName:@"JobApplication"]
jobApplication[@"applicantName"] = @"Joe Smith";
jobApplication[@"applicantResumeFile"] = file;
[jobApplication saveInBackground];

参考:iOS/OS X Developer Guide | Parse

そうしないと、せっかく保存した PFFile は一生辿ることのできないゴミファイルになってしまいます。。。

[Tips] ゴミファイルをクリーンする

残ってしまったゴミファイルは、「Dashboard > 対象のアプリケーションを選択 > Setttings > General settings」で Clean Up Filesボタンをクリックして消すことができます。

parse-ios-pffile-1

保存したPFFileを使用する

PFFileで保存したファイルを使用するには、以下の getData〜 系メソッドを使用します。

PFFile *applicantResume = anotherApplication[@"applicantResumeFile"];
NSData *resumeData = [applicantResume getData];

PFFile の getData〜 系メソッドは以下のものが用意されています。

- (NSData *)getData
同期的にデータ取得処理を行う。
可能な場合はキャッシュからデータを取得する。
- (NSInputStream *)getDataStream
同期的にデータ取得処理を行う。
NSInputStreamを使用することで、取得したデータをすぐにメモリ上にすぐに展開しないので、メモリ不足警告を避けたい場合に有効。
- (NSData *)getData:(NSError **)error
同期的にデータ取得処理を行う。
可能な場合はキャッシュからデータを取得する。
エラーが発生した場合は NSError インスタンスがセットされる。
- (NSInputStream *)getDataStream:(NSError **)error
同期的にデータ取得処理を行う。
NSInputStreamを使用することで、取得したデータをすぐにメモリ上にすぐに展開しないので、メモリ不足警告を避けたい場合に有効。
エラーが発生した場合は NSError インスタンスがセットされる。
- (void)getDataInBackgroundWithBlock:(PFDataResultBlock)block
非同期でデータ取得処理を行う。
可能な場合はキャッシュからデータを取得する。
取得処理が終了したら引数で指定したBlocksが実行される。
- (void)getDataStreamInBackgroundWithBlock:(PFDataStreamResultBlock)block
非同期でデータ取得処理を行う。
NSInputStreamを使用することで、取得したデータをすぐにメモリ上にすぐに展開しないので、メモリ不足警告を避けたい場合に有効。
取得処理が終了したら引数で指定したBlocksが実行される。
- (void)getDataInBackgroundWithBlock:(PFDataResultBlock)resultBlock progressBlock:(PFProgressBlock)progressBlock
非同期でデータ取得処理を行う。
可能な場合はキャッシュからデータを取得する。
取得処理が終了したら引数で指定したBlocksが実行される。
保存処理中は定期的にprogressBlockが呼ばれ進捗を確認できる。

- (void)getDataStreamInBackgroundWithBlock:(PFDataStreamResultBlock)resultBlock progressBlock:(PFProgressBlock)progressBlock
非同期でデータ取得処理を行う。
NSInputStreamを使用することで、取得したデータをすぐにメモリ上にすぐに展開しないので、メモリ不足警告を避けたい場合に有効。
取得処理が終了したら引数で指定したBlocksが実行される。
保存処理中は定期的にprogressBlockが呼ばれ進捗を確認できる。
- (void)getDataInBackgroundWithTarget:(id)target selector:(SEL)selector
非同期でデータ取得処理を行う。
保存処理が終了したらtargetで指定したインスタンスのselectorで指定したメソッドが実行される。

例えば、画像ファイルを取り出すには以下のように記述します。

PFFile *userImageFile = anotherPhoto[@"imageFile"];
[userImageFile getDataInBackgroundWithBlock:^(NSData *imageData, NSError *error) {
    if (!error) {
        UIImage *image = [UIImage imageWithData:imageData];
    }
}];

アップロード/ダウンロードの進捗状況を確認する

アップロード/ダウンロードの進捗状況を確認するには、- (void)saveInBackgroundWithBlock:(PFBooleanResultBlock)block progressBlock:(PFProgressBlock)progressBlockメソッドを使用します。

NSData *data = [@"Working at Parse is great!" dataUsingEncoding:NSUTF8StringEncoding];
PFFile *file = [PFFile fileWithName:@"resume.txt" data:data];
[file saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
  // Handle success or failure here ...
} progressBlock:^(int percentDone) {
  // Update your progress spinner here. percentDone will be between 0 and 100.
}];

参考:iOS/OS X Developer Guide | Parse

まとめ

PFFileは単体で削除する機能がないので、PFObjectからの参照がないPFFileに対しては、前述した通りダッシュボードからクリーンアップを要求して削除しなければなりません。 その点を除けば、PFObject同様、簡単にファイルを取り扱うことができ、非常に便利&強力な機能だと思います!