FileMakerで写真や画像の管理をする話を続けています。これまでオブジェクトのパスについて簡易な記述でお茶を濁してきました。パスのお話をもうちょっと掘り下げます。
このページ内の見出し
実作編 Ver.1(1.3)
ファイルを育てていくという後から思いついたシリーズに準じて、この投稿を改竄し、実作編第二回とします。
第一回はこちら → FileMakerメディア管理 実作編 Ver.0 基本
Ver.0で基本のDBを作りました。Ver.1 ではこの記事の内容に合わせてファイルパスをの取得を追加しています。
パスがきっちり入ることになりまして、基本のデータベースが出来上がりました。
※ パスの記述がMac仕様(POSIX)となっていますのでご注意ください ※
では改めまして、本題行きます。
ファイルパスのフィールド
メインであるメディアを格納するオブジェクトフィールドの次に重要と思われるフィールドがファイル名、そしてファイルパスです。
パスのフィールドにパスを格納しておくことで、ファイルの在処を特定したりスクリプトに使用したり分類項目を作ったり活用できます。
パスについての補足
この後、計算式でパスを取得する話を書いたわけですが、その話の前にFileMakerで扱うパスについて補足があります。
パスを取得するには、オブジェクトから読み取る、インポート時に読み込む、あるいは完全手書きで生成するといった方法があります。
ファイルメーカーの機能で取得できるパスは純粋なパスではなく、FileMaker都合の「接頭語 + パス」の形のパス(のようなもの)です。インポートで取得するパスにいたっては「FileMaker都合のパスの形」すらしていません。
フォルダから一括インポートしたときに得られるファイルパスは別口
フォルダから一括インポートするときにファイルパスも取得できます。ここで得られる「ファイルパス」と、本日ここで語ってる「ファイルパス」を、これまで完全に同じものとして扱ってきました。でもそれは大きな間違いでした。
インポートして得られるファイルパスは「 file:// 」を接頭語に持つパスです。これは「ここからインポートしたよ」という単なるメモ書きです。これはこれで大事なデータですが、パスの体を成していません。その証拠に、v19から搭載されたFileMakerパスを他形式に変換する関数を使って変換できません。FM仕様のパスですらないということです。
GetAsText(オブジェクト)で得られるパスはFileMaker仕様のパスです。すごく似ていますが根本的に異なります。
この二つを一緒にしてはいけなかったんですが一緒にしていました。そのせいでVer.3ぐらいで不整合が出てきて、パスの秘密を初めて知って、こうしてVer.1まで舞い戻って修正を施しています。
これに関してぐだぐだしつこく語ってる投稿があります。→ FileMakerメディア ファイルパス【改】
ということで、ここにご報告申し上げておきます。では引き続きどうぞ。
パスに関するフィールド
実作ファイルではパスのフィールドを設けました。次の三つです。
ファイルパス ・・・ フォルダからインポートするとき「ファイルパス」として得られるパスのようなもの。以前はこのフィールドを重要視していましたが、単にインポート時の印程度のものとしました。また、名前が紛らわしいので後のバージョンでは「インポート元パス」という名前に変更しています。
filePathFM ・・・ FM形式のパスで、接頭語を「file:」に統一したもの。このバージョンでは、こちらがパスとして重要視するフィールドになりました(※ 後のバージョンではPOSIXを基軸に変更しました)
filePosix ・・・ posix 形式のパス。
当初、フィールド定義の計算式でパスを取得するように作っていましたが変更しました。フィールドは素直なテキストにして、スクリプトで計算してフィールドに記入します。
オブジェクトからパスを取得する
この章では、オブジェクトフィールドに読み込んだメディアファイルからパスを取得する計算式を書いています。フィールド定義でもそのようにしていました。しかし後に、パスフィールドはオブジェクトに依存しないことに変更しました。
よって、以下の記述は計算式の説明として参考程度にご覧ください。
GetAsText でファイルパスを取得
FileMakerの関数やスクリプトステップに「パスを取得」というものはありません。パスを取得するには、ソースのオブジェクトフィールドを元に計算しなければなりません。
GetAsText() を使います。
GetAsText( オブジェクトフィールド )
こうするとオブジェクトフィールドの情報が改行区切りのテキストとして取得できます。内容や行数はオブジェクトによって異なりますが、大抵最後の行がパスです。
最後の行を取得するには GetValueで「数値(最後の行)」を指定します。
GetValue( GetAsText( オブジェクトフィールド ) ; 最後の行 )
最後の行を取得するには、 GetAsText( オブジェクトフィールド ) が全部で何行あるのかを調べます。全部の行数 = 最後の行 だからです。何行あるかを調べるのが ValueCount です。
ValueCount( GetAsText( オブジェクトフィールド ) )
これで 行数 = 最後の行 を得られます。先ほどの GetValueと組み合わせて
GetValue( GetAsText( オブジェクトフィールド ) ; ValueCount( GetAsText( オブジェクトフィールド ) ) )
こうなります。
これで、オブジェクトのGetAsTextの最後の行、即ち FileMakerのパスを取得できました。
・・・以上ですが、この説明でボリュームを含むパスが取得できるのは、オブジェクトの登録を「参照」で行っているときのみです。
もっとちゃんとファイルパス作成
「格納」で登録している場合、GetAsTextでは「管理 > オブジェクト…」で定義した場所より下の相対パスを返します。
オブジェクトの管理
管理 > オブジェクト… でデフォルトの格納場所を設定してありますね。

そして管理 > データベース のオブジェクトのフィールド定義でさらに格納場所を設定していますね。

そんなわけで「格納」で登録するとGetAsTextで相対パスになります。
これをそのまま「ファイルパス」フィールドに記入すると、格納方法の違いによって絶対パスと相対パスの混在になってしまいます。混在は良くないので、絶対パスに統一します。そのためにやることは以下に。
オブジェクトの登録方法による違い
オブジェクトの登録方法にはいくつか種類があります。
- ファイル内に埋め込む
- フィールド定義で設定済みの場所に格納
- 参照
ファイル内に埋め込む場合はパスもクソもないので放っておくとして、格納場所に読み込んだのか、参照で登録したのかという違いがあります。
格納には「セキュア」と「オープン」がありますが、「オープン」を前提にします。セキュアって暗号化するんですよね?無視します。
フィールド「ファイルパス」にパスを取得する計算式は、「格納」か「参照」かによって分岐させてそれぞれ別のものを作成することになります。
もし、登録方法が参照なら [ 上記の計算 ] 、 登録方法が格納なら [ 以下の計算 ]
こういう簡単な条件分岐を計算式におっ被せます。「以下の計算」については以下に書きますが、その前に「もし登録方法が格納なら、参照なら」って、それ何なん。これが先ですね。
「登録方法」フィールドを作る
オブジェクトフィールドにどのようにデータを保存したのか、その保存方法を知ることが必要です。それを示すフィールドを作成しておいてもいいですね。フィールド名はここでは仮に「登録方法」とします。
GetContainerAttribute で storageType を使うと保存方法が得られます。
GetContainerAttribute( オブジェクトフィールド ; "storageType" )
これでオブジェクトフィールドのデータ保存方法が結果に返されます。以下の四つです。
- Enbedded … 埋め込み
- External (Secure) … セキュア格納
- External (Open) … オープン格納
- File Reference … 参照
これをフィールドとして作っておくと、計算のネタ以外に分類のひとつとしても使えますね。私は見た目にやさしい分類用として「”File Reference”なら”参照” そうでなければ”格納”」と出力されるようにしています。
ということで「登録方法」を条件分岐に使ってパス作成の計算式を変更します。主に External(Open) と File Reference を条件に分岐させてやれば良いでしょう。
ではさきほどの「以下の計算」の続きいきます。
「格納」時のパスの取得
「格納」の場合は相対パスしか取得できませんから、これを「参照」時と同じように絶対パスで取得できるようにいたしましょう。
いろんなやり方があると思いますが、例えば以下のような。
やり方その1
データベースの場所
使用中のFileMakerファイルの場所を利用します。Get ( ファイルパス ) で取得できます。ファイル名まで含まれますから、ファイル名を除きます。 Get ( ファイル名 ) & “.fmp12” がファイル名ですので “” に置換して消します。
Substitute ( Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" )
これでFMファイルがあるディレクトリまでのパスが取得できました。
オブジェクトの格納フォルダ
こいつに、オブジェクト管理で指定した格納フォルダを加えます。このデフォルトの格納フォルダをゲットできるGet関数はないのでしたっけ?
Substitute (
Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" )
& "指定した格納フォルダ/"
「file:」から始まるオブジェクトフォルダまでのパスが手に入りました。
GeAsTextから相対パス
次に、GetAsText の最後の行、相対パスです。こいつを持ってきます。
GetValue ( GetAsText ; ValueCount ( GetAsText ) )
まずお馴染みGetAsTextの最終行です。「JPEG: main/0011.jpg」みたいな相対パスを得られます。冒頭の「JPEG:」は要りませんからそれを除きます。「:」を「¶」に置換して、2行目だけゲットしましょう。
GetValue ( Substitute ( GetValue ( GetAsText ; ValueCount ( GetAsText ) ) ; ":" ; ¶ ) ; 2 )
入れ子にするとこんなのですね。
さっき作った格納フォルダまでのパスとこいつを合体させます。
Substitute ( Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" ) & "指定した格納フォルダ/" & GetValue ( Substitute ( GetValue ( GetAsText ; ValueCount ( GetAsText ) ) ; ":" ; ¶ ) ; 2 )
すべて合わせると計算式はこうなりました。なんてややこしいんだ💦
これで、格納の相対パスから絶対パスを得られました。
合体させて入れ子の計算式を作りましたが、私はじつはフィールドをいくつか作って順に処理しています。フィールドを作るも良し、計算式に全部ぶち込むも良し、Let関数で順序立てるも良しです。
やり方その2
こちらはGetAsTextを使いません。計算は上記より単純です。
データベースの場所
データベースの場所は上記と同じです。これを利用します。
オブジェクトの格納フォルダ
ここがやり方その1と異なります。こちらはオブジェクト管理で設定したフォルダに、フィールド定義で設定したフォルダを加えます。オブジェクト管理で「Media」を指定していて、フィールド定義で「main/」を指定しているとすれば「Media/main/」です。これをデータベースの場所にくっつけます。
Substitute ( Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" ) & "Media/main/"
こうなります。
かんたんですが、これを手動で書くのがちょっと悔しい。オブジェクトの格納場所をゲットできるGet関数はないのでしょうか?
ないみたいなので、格納フォルダ途中パスを、グローバルフィールドに書いておくと便利です。格納するフォルダを目視で確認も出来ますし。
ファイル名
ファイル名をゲットします。すでにファイル名フィールドがあればそれでいいし、なければ GetContainerAttribute でゲットします。GetAsTextで持ってきても良いけど、今作ってる「ファイルパス」から持ってくることだけは避けましょう☺️
GetContainerAttribute ( イメージ ; "filename" )
すべて合体させるとこうなります。
Substitute ( Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" ) & 格納フォルダ & ファイル名
格納で読み込んだ場合の絶対パスを取得できました。
どちらのやり方でもパスは冒頭が「file:」になりますが気にしません。
格納と参照をifでまとめます
まとめるとこうなります。ちょっと乱暴ですが「登録方法が参照か、そうでないか」と割り切ってます。「埋め込み」や「セキュア」など、他の可能性があるなら、もうちょっと丁寧な if を作っておいてください。
if ( 登録方法 = "File Reference" ; GetValue( GetAsText( オブジェクトフィールド ) ; ValueCount( GetAsText( オブジェクトフィールド ) ) ) ; Substitute ( Get ( ファイルパス ) ; Get ( ファイル名 ) & ".fmp12" ; "" ) & 格納フォルダ & ファイル名 )
これでファイルパスフィールドに絶対パスがゲットできました。
POSIX
さて何のために絶対パスを必要とするかというと、POSIXに変換するからです。POSIXパスのフィールドを作っておけばAppleScriptやターミナルの操作に使えます。
パスの変換はFileMaker Pro v19から関数として追加されたそうですが19より前のバージョンを使っているなら計算式を作ってファイルパスから変換します。カスタム関数に作っておくといいでしょう。
変換の計算式
ファイルパスをPOSIXに変換するとき、注意する点が三つあります。
ファイル種類
FileMakerのパスは、接頭語がくっついています。純粋パスではありません。
file:
image:
movie:
filemac:
moviemac:
imagemac:
…
こんな感じで、パスへと続きます。邪魔です。パス変換の際にはすべて要りません。
// はパスではない
フォルダインポートで取得できるファイルパスは「file://」となっていて、特に「//」これのせいでパスと認識されません。パス変換の関数も動きません。「//」は「/」に置換します。
起動ディスクかどうか
POSIXでは、外部ボリュームの場合「/Volumes/ボリューム名/」で始まりますね。起動ボリュームの場合はドライブ名をすっ飛ばして「 /Users/ 」 から始まります。
POSIXへのパス変換では、システムドライブ上であるのか外部ドライブであるのかが重要で、それによって計算を分ける必要があります。
計算式
上記を踏まえて、ファイルパスをPOSIXに変換する計算式を作ります。流れはこうです。
1 FileMaker式のパスの「//」があれば「/」に置換します。これはフォルダインポートの際に取得するパスのようなものをパスの形にします。
2 面倒な接頭語をすべて排除します。「:」 を ¶(改行)に置換して、2行目だけをいただきます。
3 次にシステムドライブの場合のパスを作ります。システムドライブの場合は、ドライブ名を排除します。
4 次に外部ドライブの場合のパスを作ります。冒頭に「/Volumes」を付け足します。
5 目的のFMパスがシステムドライブを含んでいればシステムドライブ用、そうでなければ外部ドライブ用のパスを採用します。
Let関数を使って順番に書くとこんな感じですか。
Let ( [ FMP = FileMakerのパス ; Pa = GetValue(Substitute ( FMP ; [ "//" ; "/" ] ; [ ":/" ; ¶ & "/"] ) ; 2 ); ps = Substitute ( Pa ; Get ( システムドライブ ) ; "/" ) ; pv = "/Volumes" & Pa ]; If ( PatternCount ( Pa ; Get ( システムドライブ ) ) ; ps ; pv) )
ファイルパスフィールドとPOSIXフィールドは、メディア管理を行う上で非常に重要ですので、是非とも作成しておくことをお勧めします。v19からはパス変換の関数が登場しましたので、すべてv19以降でのみ動かす前提なら関数を使えばいいですね。
パスから得られる情報
で、パスフィールドがあることで得られる情報や恩恵についてのオマケです。
ファイル名
パスからファイル名を得られますが、ファイル名はオブジェクトから直接得ることをお勧めします。だってファイルパスを作るのにファイル名を使ってますから。合わせ鏡の鶏と卵になっちまいます。
種類、拡張子
ファイル名に拡張子がある場合は、最後の「 . 」以降が拡張子なので「 . 」を改行に置換して最後の行をゲットすれば得られますね。拡張子がないファイルもたまにありますからこれもほんとは別のやり方でゲットするのがいいのですけど。
フォルダ
「/」を改行に置換すると、フォルダ階層が改行区切りで得られます。最後の行 – 1 が、すぐ上のフォルダ名になりますね。 -2 にするとフォルダのフォルダが得られます。フォルダ名を得ることでいろいろ分類やフィルターに使用できて管理上便利です。
AppleScriptやターミナルやFileMakerで使う
POSIXフィールドがあると、フィールドを指定するだけでAppleScriptやターミナルとのやりとりに利用できます。「Finderで表示」「ゴミ箱に入れる」「ファイルにコメントを書き入れる」などファイル操作、ターミナルを使っての「Exifを網羅して取得」など広がります。
FileMaker 18 以降は外部のファイル操作ができるようになったので「名前の変更」「捨てる」「存在有無の確認」など一部ファイル操作が内製の機能だけでできるようになりました。