FileMakerメディア管理 実作編 Ver.1 ファイルパスを取得する

FileMakerで写真や画像の管理をする話を続けています。これまでオブジェクトのパスについて簡易な記述でお茶を濁してきました。「ファイルパス」というフィールドにパスを自動格納するお話をもうちょっと掘り下げます。

その前に改変のお知らせです。後付けで「実作編」というシリーズを思いついたことに合わせて、このポストを二回目ということにしました。添付ファイルも差し替えました。

実作編 Ver.1

ファイルを育てていくという後から思いついたシリーズに準じて、この投稿を改竄し、実作編第二回とします。

第一回はこちら → FileMakerメディア管理 実作編 Ver.0 基本

Ver.0で基本のDBを作りました。Ver.1 ではこの記事の内容に合わせてファイルパスをの取得を追加しています。

パスがきっちり入ることになりまして、基本のデータベースが出来上がりました。

(以前、ここに上げていたファイルは一旦取り下げて V1 ファイルに差し替えました)

FileMaker Document
DownloadMdediaDBv1.zip

では改めまして、本題行きます。

ファイルパスのフィールド

パスフィールド

メインであるメディアを格納するオブジェクトフィールドの次に重要と思われるフィールドがファイル名、そしてファイルパスです。

パスのフィールドにパスを格納しておくことで、ファイルの在処を特定したりスクリプトに使用したり分類項目を作ったり活用できます。

手動でパスをタイプしてもいいんですよ。いいんですけど、良くない。自動で勝手に入力してくれないと甲斐がありません。

GetAsText でファイルパスを取得

FileMakerの関数やスクリプトステップに「パスを取得」というものはありません。パスを取得するには、ソースのオブジェクトフィールドを元に計算しなければなりません。

GetAsText() を使います。

GetAsText( オブジェクトフィールド )

こうするとオブジェクトフィールドの情報が改行区切りのテキストとして取得できます。内容や行数はオブジェクトによって異なりますが、大抵最後の行がパスです。

最後の行を取得するには GetValueで「数値(最後の行)」を指定します。

GetValue( GetAsText( オブジェクトフィールド ) ; 最後の行 )

最後の行を取得するには、 GetAsText( オブジェクトフィールド ) が全部で何行あるのかを調べます。全部の行数 = 最後の行 だからです。何行あるかを調べるのが ValueCount です。

ValueCount( GetAsText( オブジェクトフィールド ) )

これで 行数 = 最後の行 を得られます。先ほどの GetValueと組み合わせて

GetValue( GetAsText( オブジェクトフィールド ) ; ValueCount( GetAsText( オブジェクトフィールド ) ) )

こうなります。

これで、オブジェクトのGetAsTextの最後の行、即ち FileMakerのパスを取得できました。

・・・という説明をこれまで書いてきましたが、この説明でボリュームを含むパスが取得できるのは、オブジェクトの登録を「参照」で行っているときのみです。

もっとちゃんとファイルパス作成

格納」で登録している場合、GetAsTextでは「管理 > オブジェクト…」で定義した場所より下の相対パスを返します。

オブジェクトの管理

管理 > オブジェクト… でデフォルトの格納場所を設定してありますね。

オブジェクト管理
データベースファイルがある同じ階層のMediaフォルダを指定いる例。「格納」では「Media」以下の相対パスになります。

ここで指定したフォルダがカレントになり、それより下の階層が相対パスになります。

実はこの画面で、フォルダを「なし」にしてみたかったんですよ。でもできません。[データベースの場所] にフォルダを少なくとも一個作る必要があります。

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

オブジェクト-格納 設定画面
オブジェクト管理で指定したMediaフォルダの下にmainフォルダを指定した例。図ではうっかり /main/ となっていますが、ここは main/ でしょうね・・

ここで指定した「main」は相対パスの中に入ってきます。「main/0011.jpg」のようになります。

さてそんなわけで「格納」で登録するとGetAsTextで相対パスになります。

相対パスをパスとして受け取ってもあまり役に立ちません。いえ、相対パスでも役に立ちます。基本OKです。でもメディアの登録方法の違いで「ファイルパス」フィールドの内容が絶対パスと相対パスの混在になってしまいます。その後の展開で不利です。

その後の展開というのは、「ファイルパス」フィールドを元にPOSIXパスを作成しますので、そのときに困ります。「ファイルパス」を絶対パスに統一しておくのに越したことはありません。

オブジェクトの登録方法による違い

オブジェクトの登録方法にはいくつか種類があります。

  • ファイル内に埋め込む
  • フィールド定義で設定済みの場所に格納
  • 参照

ファイル内に埋め込む場合はパスもクソもないので放っておくとして、格納場所に読み込んだのか、参照で登録したのかという違いがあります。

格納には「セキュア」と「オープン」がありますが、「オープン」を前提にします。セキュアって暗号化するんですよね?無視します。

フィールド「ファイルパス」にパスを取得する計算式は、「格納」か「参照」かによって分岐させてそれぞれ別のものを作成することになります。

もし、登録方法が参照なら [ 上記の計算 ] 、
登録方法が格納なら [ 以下の計算 ]

こういう簡単な条件分岐を計算式におっ被せます。「以下の計算」については以下に書きますが、その前に「もし登録方法が格納なら、参照なら」って、それ何なん。これが先ですね。

「登録方法」フィールドを作る

オブジェクトフィールドにどのようにデータを保存したのか、その保存方法を知ることが必要です。それを示すフィールドを作成しておいてもいいですね。フィールド名はここでは仮に「登録方法」とします。

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やターミナルの操作に使えます。

POSIXへの変換はFileMaker Pro v19から関数として追加されたそうですが19より前のバージョンを使っているなら計算式を作ってファイルパスから変換します。

FileMakerのパスはややこしいので、ちょっとくどくどしい置換の計算式を作っています。これが絶対に正しいという自信はありません。

Substituteを使った置換

ファイルパスをPOSIXに変換するとき、注意する点が三つあります。

ファイル種類

FileMakerのパスは、最初の文言がファイルタイプによっていろいろあります。

file:
image:
movie:
filemac:
moviemac:
imagemac:

こんな感じで、パスへと続きます。POSIX変換の際にはすべて要らないものです。これら冒頭の文言をすべて羅列してやらねばなりません。ここが中途半端だと変換に失敗します。

スラッシュの数

文言の他にもうひとつ注意することがあります。スラッシュの数です。

file:/ の場合もあれば file:// の場合もあります。これは通常ボリュームかネットワークボリュームかの違いのようです。

起動ディスクかどうか

通常パスは「/Volumes/ボリューム名/」で始まりますね。起動ボリュームの場合、それでは都合が悪いこともありまして、「/Users/」から始めたいんです。

その理由は、iCloud、DropBox、Nextcloud といったネットストレージと同期させているフォルダをよく使うからです。別のMacから同じユーザーでファイルを開くとボリューム名が変わってしまいますから。

ですので、POSIXへのパス変換では、システムディスク上であるのか外部ディスクであるのかが重要で、それによって計算を分ける必要があります。

クラウドのフォルダを使わない人にとってはどうでもいい話ですので、必要ならということで。あと、外部ディスクにクラウドの同期フォルダがある人も別の処理が必要ですね。

上記注意点を踏まえて、ファイルパスをPOSIXに変換する計算式を作ります。

全体を if で包みます。内容は「システムディスクか、そうでないか」です。これで分岐しますね。

if ( PatternCount( ファイルパス ; Get( システムドライブ )) ;
;
)

まずは「//」があれば「/」に統一します。
次に、image: file: moviemac: みたいな冒頭の文言を “” に置換して消します。
最後に システムドライブ( /ドライブ名/ )を / で置き換えます

システムディスクではない場合、まず最初に「//」を「/」で整えた後、
image: movie: file: filemac: みたいな文言をすべて 「/Volumes」に置き換えます。

If ( PatternCount ( ファイルパス ; Get ( システムドライブ ) ) ; 
 Substitute ( ファイルパス ; 
 [ "//" ; "/" ] ;
 [ "image:" ; "" ] ;
 [ "movie:" ; "" ] ;
 [ "file:" ; "" ] ;
 [ "filemac:" ; "" ] ;
 [ "moviemac:" ; "" ] ;
 [ "imagemac:" ; "" ] ;
 [ Get ( システムドライブ ) ; "/" ] 
 )
; 
 Substitute ( ファイルパス ;  
 [ "//" ; "/" ] ;
 [ "image:" ; "/Volumes" ] ;
 [ "movie:" ; "/Volumes" ] ;
 [ "file:" ; "/Volumes" ] ;
 [ "filemac:" ; "/Volumes" ] ;
 [ "moviemac:" ; "/Volumes" ] ;
 [ "imagemac:" ; "/Volumes" ] 
 )
)

こんなのでええのかと思わなくもないですが何とかこれでいけてます。

ただ、コマンドの中で使用するときなどはスペースや記号をエスケープ処理しないといけないので [ ” ” ; “\ ” ] [ “(” ; “\(“] など加えていったりもしています。行き当たりばったりですが基本の理屈をよく判っていないので。posixへの変換、こんなのじゃなくちゃんといた賢いやりかたをご存じの天才の方はぜひ教えてください。v19を買えば済むって?はい。

ファイルパスフィールドとPOSIXフィールドは、メディア管理を行う上で非常に重要ですので、是非とも作成しておくことをお勧めします。

パスから得られる情報

で、パスフィールドがあることで得られる情報や恩恵についてのオマケです。

ファイル名

パスからファイル名を得られますが、ファイル名はオブジェクトから直接得ることをお勧めします。だってファイルパスを作るのにファイル名を使ってますから。合わせ鏡の鶏と卵になっちまいます。

種類、拡張子

ファイル名に拡張子がある場合は、最後の「 . 」以降が拡張子なので「 . 」を改行に置換して最後の行をゲットすれば得られますね。拡張子がないファイルもたまにありますからこれもほんとは別のやり方でゲットするのがいいのですけど。

フォルダ

「/」を改行に置換すると、フォルダ階層が改行区切りで得られます。最後の行 – 1 が、すぐ上のフォルダ名になりますね。 -2 にするとフォルダのフォルダが得られます。フォルダ名を得ることでいろいろ分類やフィルターに使用できて管理上便利です。

AppleScriptやターミナルやFileMakerで使う

POSIXフィールドがあると、フィールドを指定するだけでAppleScriptやターミナルとのやりとりに利用できます。「Finderで表示」「ゴミ箱に入れる」「ファイルにコメントを書き入れる」などファイル操作、ターミナルを使っての「Exifを網羅して取得」など広がります。

FileMaker 18 以降は外部のファイル操作ができるようになったので「名前の変更」「捨てる」「存在有無の確認」など一部ファイル操作が内製の機能だけでできるようになりました。