FileMakerで写真や画像の管理システムを作る話を続けています。これまでオブジェクトのパスについて簡易な記述でお茶を濁してきました。パスのお話をもうちょっと掘り下げます。
はじめに
このページではファイルパスについて少し掘り下げます。また、以下のページを補足する投稿となっています。
ファイルパスについて
画像のデータベースを作るに当たって、最重要なフィールドがファイルパスです。
ダウンロードできる実作ファイルにはすでに「ファイルパス」というフィールドがあります。これが何なのかという問題を含めて、新たに次のフィールドを作成しました。
- filePathFM … FileMaker 仕様のパスです。
- filePosix … UNIX形式のPOSIXです。
- 登録方法 … FileMaker にどのように登録したかです。
ファイルパスフィールドがすでにあるのに filePathFM というフィールドを作るのは何故なのか。以下、FileMakerのパスの秘密が明らかに。
パスとは
パスはファイルの在処を示す記述で、ファイルを唯一絶対かつ「それはそれ自身と同一である」という宇宙真理的アイデンティティを確保する、いわばコンピュータの根幹ですね。
・・・んで、パスの書き方にはいろいろあって、Macでは現在UNIXの形式POSIXが主流です。こんな感じの記述ですね。
/Users/username/UntitleFolder/image.jpg /Volumes/diskB/UntitleFolder/image.jpg
POSIXの特徴は起動ボリュームの場合ユーザーフォルダから記述がスタートすることです。これが何かと都合が良かったり悪かったりします。
MacOS時代は「:」で区切った書き方でした。今でもAppleScriptで使えると思います。DOSでは「¥」で区切る書き方もありまして、昔¥で区切るテキストの見た目がすごく嫌いで、それで早々にDOSから逃げ出したという思い出もあります(どうでもいい)
実作ファイルではパスを記録するフィールドがいくつもあります。POSIX形式、FileMaker独自仕様パス形式、FileMaker独自の謎のパスのようなもの形式、いろいろあります。これらについて多少の知見を得たのでこの投稿も随時大幅改編しておる次第です。
FileMaker パス事情
ずっと誤解していて後に真実を認識したパスの物語をはじめに少しお話しておきます。
FileMaker でパスを取得する手段は二つあります。一つは GetAsText 関数でオブジェクトから取得します。
GetAsText( オブジェクトフィールド )
数行の情報が手に入り、その中にパスもあります。
もう一つは「フォルダからインポート」するときに「ファイルパス」をインポートします。
パスというものはファイルの場所を特定する唯一無二の絶対的なものであるという認識だったので、FileMaker が示すパスの節操のなさに最初は戸惑いました。つまり、上記二つの方法でゲットするパスの形に一貫性がないのです。
この件に関してぐだぐだ語ってる投稿があります。→ FileMakerメディア ファイルパス【改】 が、ここを読んでいる人は読みに行かなくてもいいです。ここでまとめているからです。
FM仕様のパス
FileMakerでは 接頭語 + パス の形がパスです。ファイルの種類によって接頭語が変わります。パス部分はボリュームレベルからの階層を示す一般的な形のパスです。接頭語なしではFileMakerはそれをパスと認識しません。file: だったり mac: だったり image: だったり、必ず接頭語がくっつきます。
まずここを理解しておくことが大切です。接頭語 + パス のみがFileMakerのパスです。FileMaker において、パスは「唯一絶対的な対象の特定」をするものはなく、URL のような、特定の目的や対象の種類によって変化する命令の一種であるということです。
フォルダパスを取得で取得しタパス
それを端的に表すのが、スクリプトステップ「フォルダパスを取得」です。「フォルダパスを取得」では接頭語のない純粋パスを取得します。取得したは良いが、ここに罠があります。
純粋パスは FileMaker ではパスとして認識されません。
取得した後、接頭語を付け足す必要があります(機能によってはそのままで使えるものもあります。例外的に)
どの接頭語を付け足せば良いか、対象が何であるかによって代わってしまいます。どうすりゃいいんだいと誰もが躓きます。
最も汎用性の高い接頭語は「file:」です。まず大抵「file:」を頭にくっ付けると間違いはおきません。
インポート時に取得したファイルパス
FileMakerではフォルダを指定して中身のファイルをごっそり登録できます。このときファイルパスを含めることもできます。
フォルダからインポートする際に取得できる「ファイルパス」は「file:/」の接頭語になります。file:/ に、本来の / から始まるパスがくっつきますから、見た目として「file://」で始まるように見えます。そしてここに罠があります。
「file://」が付いたパスは、当のFileMakerでさえ、それをパスと認識しません。
FileMaker v19 でパス形式を変換する関数が登場しましたが、この「ファイルパス」は変換できません。パスと認識されていないんです。
「file://」を置換関数で「file:/」にしてやるとFileMakerが認識できるパスになります。そうか、じゃあ file:// で始まる謎パスが格納された「ファイルパス」フィールドを変更すればいいんだな。と、思ったこともありました。いいえ、早まってはいけません。
この file:// 式謎パスの存在意義は不明ですが、しかし大事な役割がありました。
フォルダインポートの際「更新」では照合フィールドを指定しますね。この「file://」式パスのフィールドを照合フィールドに指定できます。「file:/」では照合フィールドとして使えません。なんてこったい。
そんなわけで、フォルダインポートで取得した「ファイルパス」フィールドはそのまま温存し、新たにFMパス形式の「filePathFM」フィールドと、POSIXに変換した「filePosix」フィールドを追加したわけです。
ややこしい話の最中に恐縮ですが、パスを照合フィールドに指定することに関してはさらなる罠があります。照合を間違えるんです。その症状と原因と解決についてはこちらの投稿をご覧ください。
→ FileMakerで正しくリレーションできない照合の問題、原因と解決【重要】
FileMakerのパス事情、なんて複雑なんでしょう。
この記事を最初に書いた昔はその複雑さを知らず、FM仕様のパスとインポートで作られるファイルパスをごっちゃにしていました。
ということで、大幅追記したFileMakerパスについての汎用的な話でした。
ここから先は実作メディア管理の具体的な話になります。
追加したフィールド
というわけで、新たに追加したパスのフィールドは複雑さを含まない純粋ピュアなパスフィールドです。
filePathFM
R2ファイルで作った filePathFM は、そんな複雑さを一掃したフィールドです。つまり filePathFM フィールドでは接頭語を「 file: 」に統一したFMパスを格納します。
そのパスが相対パスなら file:ボリューム名…、絶対パスなら file:/ボリューム名… となります。
このフィールドはスクリプトや計算式で利便性を発揮します。
filePOSIX
Macで標準のUNIX形式のPOSIXパスです。外部コマンドを使用するようになると必要になります。
POSIX では、システムボリュームはボリュームをすっ飛ばして /Users/ から始まります。FM仕様では、ボリュームから順に記します。ボリュームを省略するPOSIXが有利なのは、iCloud などクラウドストレージにメディアファイルを置いているときです。別のMacから同じユーザー名で使うとき、元ファイルが行方不明になりません。
ファイルパス
そんでもって、最初からある「ファイルパス」ですが、昔パスの秘密を知らない頃このフィールドに「フォルダインポートで取得したパス」と「GetAsTextで取得したパス」をごちゃ混ぜにしていました。今では知見も得たので「フォルダインポートで取得したファイルパス」だけが入っています。
名前がややこしいので後に「インポート元パス」と改名され、ここからインポートしたよという印の役割となりました。
登録方法
さてここで「登録方法」という大事なフィールドについてです。FileMakerにメディアファイルをどのように登録したかの記録フィールドです。
登録方法
オブジェクトの登録方法にはいくつか種類があります。
- ファイル内に埋め込む
- フィールド定義で設定済みの場所に格納する
- フィールド定義で設定済みの場所に暗号化して格納する
- 参照する
実作編ファイルではファイル内に埋め込まず、セキュアでない格納場所を指定するので、格納か参照かの二択になります。
もし、登録方法が参照なら 〜 登録方法が格納なら 〜
「格納」か「参照」かによって、パスを作り出す計算式が変わります。それ以外でも、運用上いろんな場面で分岐しなくてはならない局面が多いのでこのフィールドはあったほうが良いんです。
「登録方法」フィールド
オブジェクトフィールドにどのようにデータを保存したのか、オブジェクトから計算できます。
GetContainerAttribute 関数で storageType を指定します。
GetContainerAttribute( オブジェクトフィールド ; "storageType" )
これでオブジェクトフィールドのデータ保存方法が結果に返されます。
- Enbedded … 埋め込み
- External (Secure) … セキュア格納
- External (Open) … オープン格納
- File Reference … 参照
この結果から、見た目にやさしい分類用として、”File Reference”なら”参照”、そうでなければ”格納” と出力されるようにしています。
※ 後々、「登録方法」フィールドは参照なら 0 格納なら 1 と、数字になりました。
オブジェクトからパスを取得する
オブジェクトからパスを取得します。
参照のとき
GetAsText でファイルパスを取得
※ 追記:
以下、GetAsText でパスを取得する方法を書いていますが、やや不備があります。「大抵最後の行がパスです」です。「大抵」と逃げていますので、より詳しくは GetAsText ( オブジェクト ) の秘密 [FileMaker] をご参照のほど、お願いいたします。
オブジェクトからパスを得るには GetAsText() を使います。
GetAsText( オブジェクトフィールド )
オブジェクトフィールドの情報が改行区切りのテキストとして取得できます。内容や行数はオブジェクトによって異なりますが、大抵最後の行がパスです。
最後の行を取得するには、結果が全部で何行あるのかを調べます。全部の行数 = 最後の行 だからです。何行あるかを調べるのが ValueCount です。
ValueCount( GetAsText( オブジェクトフィールド ) )
先ほどの GetValueと組み合わせてこうなります。
GetValue( GetAsText( オブジェクトフィールド ) ; ValueCount( GetAsText( オブジェクトフィールド ) )
これで、オブジェクトのGetAsTextの最後の行を取得できました。パスですがこれでは使い物になりません。接頭語にどんな文言がくっ付いているのかわかりませんので、「file:」に統一します。
「:」を ¶(改行)に置換して2行目をゲットしてから、「file:」をくっつけます。
"file:" & GetValue ( Substitute ( 最後の行 ; ":" ; ¶ ) ; 2 )
「最後の行」で端折ってるところをまとめて書くとこうなります。
"file:" & GetValue ( Substitute ( GetValue( GetAsText( オブジェクトフィールド ) ; ValueCount( GetAsText( オブジェクトフィールド ) ) ; ":" ; ¶ ) ; 2 )
FM仕様のパスとなりました。
インポート元パスからパスを取得
それ以外の方法として、フォルダインポートの際にインポート元パスをゲットできていますので、もしオブジェクトの場所がインポート元から変更がない場合は、これを使ってもパスが得られます。
インポート元パスの「file:/」を「file:」に置換します。
Substitute ( ファイルパス ; "file:/" ; "file:" )
FM仕様のパスになりました。
格納のとき
「格納」で登録している場合、GetAsTextでは「管理 > オブジェクト…」で定義した場所より下の相対パスを返します。
オブジェクトの管理
管理 > オブジェクト… でデフォルトの格納場所を設定してありますね。
そして管理 > データベース のオブジェクトのフィールド定義でさらに格納場所を設定していますね。
「格納」時のパスの取得
「格納」の場合は GetAsText で相対パスしか取得できませんから、これを「参照」と同じように絶対パスにいたしましょう。
いろんなやり方があると思いますが、例えば以下のような。
格納フォルダのパス
データベース管理のオブジェクトのオプション「データの格納」を見ると、管理 > オブジェクト で指定したフォルダが上段に、フィールド独自の格納場所が下段に表示されます。
管理>オブジェクト で指定したフォルダまでのパス + GetAsTextの相対パス
GetAsText で得られる相対パスは下段を含みます。「main/ファイル名.jpg」こんな感じです。
ですので、上段、つまりオブジェクトの管理で設定したフォルダまでのパスを作っておけば良いわけです。
[データベースがある場所] 直下の場合
「このデータベースのパス」直下にフォルダを作っている場合はデータベースの場所を含めたパスを作成します。
Get ( ファイルパス ) からファイル名を除いたものがフォルダパスです。
Substitute ( Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" )
これでファイルがあるディレクトリまでのパス、即ち [データベースの場所] のパスが取得できました。
これに管理>オブジェクトで指定したフォルダ を書き加えるだけでオブジェクトフォルダパスが手に入りました。
Substitute (
Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" )
& "指定した格納フォルダ/"
[このデータベースの場所] と無関係な場所の場合
[このデータベースの場所] と無関係な場所に設定した場合は file: で始まるフォルダパスが書かれているはずですので、それがそのままフォルダパスです。
グローバルなフィールドに記載しておく
管理 > オブジェクトで指定したフォルダまたはパスを、そのままグローバルなフィールドに書き記しておけばいいでしょう。
フォルダ、または file: で始まるパスが書かれます。file: で始まっていればそれはそのままFMパスです。そうでなければ [データベースの場所]直下ということで、上に書いた計算でパスが導き出されます。その条件を計算フィールド「オブジェクトフォルダpathFM」に仕込むことで、どちらにせよFM形式のパスを得られます。
「オブジェクトフォルダのパス」がグローバルフィールドに鎮座することで、メインテーブルでメディアファイルのパスを取得することが容易になります。たとえ格納でのインポートであっても、ファイルパスを完璧ゲット。
GeAsTextから相対パス
オブジェクトフォルダのパスを確保できました。これに メディアファイルの相対パスを持ってきて繋げます。
GetValue ( GetAsText(オブジェクトフィールド) ; ValueCount ( GetAsText(オブジェクトフィールド) ) )
GetAsText結果の最終行を取得します。接頭語の「JPEG:」とか「file:」とかは要りませんからそれを除きます。「:」を「¶」に置換して、2行目だけいただきましょう。
GetValue ( Substitute ( GetValue ( GetAsText(オブジェクトフィールド) ; ValueCount ( GetAsText(オブジェクトフィールド) ) ) ; ":" ; ¶ ) ; 2 )
さっき作った格納フォルダパスにこいつを合体させます。
設定::オブジェクトフォルダpathFM & GetValue ( Substitute ( GetValue ( GetAsText(オブジェクトフィールド) ; ValueCount ( GetAsText(オブジェクトフィールド) ) ) ; ":" ; ¶ ) ; 2 )
すべて合わせると計算式はこうなりました。
これで、格納の相対パスから絶対パスを得られました。
以上で本日お仕舞いです。
以下は、FileMakerパスをPOSIXに変換する話を書いています。FileMaker Pro v19 以降は関数が追加されましたので不要な章となりました。一応残しておきます。
POSIX に変換
さてFMパスからPOSIXに変換して filePosix フィールドに格納しておきましょう。POSIXパスのフィールドを作っておけばAppleScriptやターミナルの操作に使えます。
変換の計算式
ファイルパスをPOSIXに変換するとき、注意する点が三つあります。
ファイル種類
FileMakerのパスは、接頭語がくっついています。
file:
image:
movie:
filemac:
moviemac:
imagemac:
…
こんな感じで、パスへと続きます。邪魔です。パス変換の際にはすべて要りません。
※ 改編した本ポストでは、file: に統一するとすでに書いています。
// はパスではない
フォルダインポートで取得できるファイルパスは「file://」となっていて、特に「//」これのせいでパスと認識されません。パス変換の関数も動きません。「//」は「/」に置換します。
起動ディスクかどうか
POSIXでは、外部ボリュームの場合「/Volumes/ボリューム名/」で始まりますね。起動ボリュームの場合はドライブ名をすっ飛ばして「 /Users/ 」 から始まります。
POSIXへのパス変換では、システムドライブ上であるのか外部ドライブであるのかが重要で、それによって計算を分ける必要があります。
計算式
上記を踏まえて、ファイルパスをPOSIXに変換する計算式を作ります。流れはこうです。
1 FileMaker式のパスの「//」があれば「/」に置換します。これはフォルダインポートの際に取得するパスのようなものをパスの形にします。
2 面倒な接頭語をすべて排除します。「:」 を ¶(改行)に置換して、2行目だけをいただきます。
3 次にシステムドライブの場合のパスを作ります。システムドライブの場合は、ドライブ名を排除します。
4 次に外部ドライブの場合のパスを作ります。冒頭に「/Volumes」を付け足します。
5 目的のFMパスがシステムドライブを含んでいればシステムドライブ用、そうでなければ外部ドライブ用のパスを採用します。
Let関数を使って、こんな感じですか( // を / に変換は 、FMパスを作る際に済ませているという前提なので省略しています)
Let ( [ fm = FM_path; fm = GetValue ( Substitute ( fm ; ":" ; ¶ ) ; 2 ); d = If ( PatternCount ( fm ; Get ( システムドライブ ) ) ; "sd" ; "d" ) ; posix = If ( d = "sd" ; Substitute ( fm ; Get ( システムドライブ ) ; "/" ) ;"/Volumes" & fm ) ]; posix )
ファイルパスフィールドとPOSIXフィールドは、メディア管理を行う上で非常に重要ですので、是非とも作成しておくことをお勧めします。
パスから得られる情報
ここまでおつかれさまでした。パスをフィールドにゲットできました。
パスフィールドがあることで得られる情報や恩恵についてのオマケです。
フォルダ
「/」を改行に置換すると、フォルダ階層が改行区切りで得られます。最後の行 – 1 が、すぐ上のフォルダ名になりますね。 -2 にするとフォルダのフォルダが得られます。フォルダ名を得ることでいろいろ分類やフィルターに使用できて管理上便利です。
存在チェック
FIleMaker v18 以降はファイルパスを指定して存在チェックができるようになりましたね。これは嬉しい機能です。メディア管理では存在チェックがかかせません。
ファイル操作
同じく v18 以降、ファイルの操作が可能になりました。ファイル名を変えたり捨てたりできます。これはすごく便利です。ところで上のほうで「オブジェクト根幹主義」「パス根幹主義」などと言っていますが、まさにこれですね。例えばファイル名を変更するでしょ、その変更したファイル名のファイルを再度読み込んだりするでしょ、オブジェクトを入れ替えますね。これこそ「パス主義」でないとできないことです。全ての根幹がオブジェクトだったら、ファイル名を変えた時点でもうオブジェクトはなくなります。「再挿入」という概念自体が成り立たなくなってしまいますね。
AppleScriptやターミナルやFileMakerで使う
POSIXフィールドがあると、フィールドを指定するだけでAppleScriptやターミナルとのやりとりに利用できます。各種ファイル操作、ターミナルを使ってのメタデータの取得、その他いろいろと可能性が広がります。POSIX必須です。
INDEX
リセットしてリスタートしてリメイクしてリブートした実作編のインデックスができました。こちらが総合案内になります。