Save as PDF and insert file into container

Ran into an issue yesterday programming in FMP 19.

A container field is set to store external attachments, unencrypted (open) format in a child folder next to the database, e.g.

.../MyData/MyDatabase.fmp12
../MyData/Attachments/[recordID]/Printout.pdf

Goal:

  • save a layout as PDF file
  • insert the file into a container record (which is in a related table)

Method 1: Save file to destination, insert Using Portal

  • set variable $path to the desired path in the proper attachment folder
  • go to print layout / enter Preview mode
  • save record as PDF ($path)
  • go to layout (original)
  • enter browse mode
  • Go to field (portal::attachment)
  • Go to Portal Row (Last)
  • Insert File ($path)

Problems:

  • you have to hard-code the attachments path, and if you change it (by editing the Container field's definition) you would need to update the script
  • file is inserted, but since the file was already at the destination, you end up with another copy, e.g.
../MyData/Attachments/[recordID]/Printout.pdf
../MyData/Attachments/[recordID]/Printout_2.pdf

This makes sense, as when you insert the file, FileMaker copies it to the destination, but since the file already exists, it makes the _2 variation.

Method 2: Save file to the destination, insert Using Portal as reference

  • same steps as above
  • Insert File ($path, dialog options / storage options: Store as Reference)

Issues:

  • oddly, even though you have set "Dialog Options" FileMaker doesn't show the dialog when you set "Storage options/Reference" which is nice.
  • the file is in the container field, but since it's stored as a reference, the behavior is different than other files in container fields (e.g. if you clear the container field's data, the file on disk is not deleted, whereas for attachments manually dragged to the container field, clearing the field data also deletes the file on disk. This inconsistency is confusing.

Method 3: Save file to temporary destination, insert Using Portal

  • set variable $tempPath = Get(TemporaryPath) & "Printout.pdf"
  • save records as PDF
  • Insert File ($tempPath)
  • Delete File ($tempPath)

Issues:

  • the Delete File step fails (presumably FileMaker is still copying the file) - if you add a delay then it works, but it's not clear how much of a delay to add, and hard-coded delays are generally a bad idea.
  • also, if the script ever fails, and the temp file is on disk, then when you repeat the script, you get a different filename in the container field, e.g.
    Printout_2.pdf
    Printout_3.pdf
    etc.

There must be an easier and less error-prone way to do this, right?

Method 3 is the way to go in my opinion. File deletion can be handled via a loop that exits when successful, with a short pause between attempts. Be aware, though, that this approach doesn't work on the server (insert and export field content are incompatible).

I would avoid methods 1 and 2. You should not be playing in managed folders. You could potentially interfere with the folders' management and the folders' management could potentially interfere with your expectations regarding the files you place there.

Regarding the following:

You are clearing the field's content in both cases.

In the first, the field contains a reference to a file on a datastore managed by FileMaker. FileMaker deletes the file in the datastore when you clear the field. FileMaker manages folders of external storage. That is why, for external storage, files are deleted in folders when a file is cleared in a field.

In the second, the field contains a reference to an unmanaged file on a datastore. FileMaker manages the reference but not the referenced file. The reference is deleted when you clear the field. The file it points to isn't as it is not managed.

FileMaker makes no assumption as to the purpose of folders where unmanaged files reside. Unmanaged files could be in a shared folder or volume and could even be used by other applications. As such, it is important for FileMaker to leave unmanaged files alone. Unmanaged files residing in folders used for external storage are unfortunate coincidences, considering only FileMaker should play in such folders.

1 Like

Good ideas.

I do want the files to be stored as copies / managed, rather than by reference.

However, another problem I'm seeing is that it becomes difficult to do anything with those stored / managed files later on.

For example, suppose I want to send an email with the files as attachments:

  • the Send Email script step allows you to add multiple attachments, for which you need a paragraph-delimited list of full path names.
  • If you GetAsText(attachments::container) you get some info, which includes the filename, but does not include the actual path to the attachment file.
  • also, the GetAsText(attachments::container) format is inconsistent, for example with a PDF you see this
remote:136563583_summary.pdf
size:612,792
JPEG:136563583/136563583_summary.jpg
PDF :136563583/136563583_summary.pdf
PNGf:136563583/136563583_summary.png
  • but with a DOC file you see this:
remote:136563583_CoverSheet_BR.doc
FILE:136563583/136563583_CoverSheet_BR.doc

This is not impossible to parse, but kind of weird that it depends on file type.

Method 3 absolutely - with a couple minor changes.
The temporary path variable is automatically cleaned up on FMP restart, and the same on FMS, however, as FMS is not restarted frequently, you end up with a lot of unused files.
Personally, I prefer the Get (DocumentsPath) then do the delete housekeeping myself.

Yes, the temp folder gets cleared, however probably not fast enough to avoid the FileName, FileName1, FileName2 and so on issue noted in the original post. That is why you want a script to delete the file (or simply rename a file) when it is done with the file. The same could be said of any folder selected for the purposes of importing, exporting, etc.

The general process of doing anything with managed files goes like this:

  1. Export the field content to some folder (temp, document, whatever);
  2. Do what you want with the file;
  3. Delete the exported file.

This process is a bit more complicated on the server due to its incompatibility with Export Field Content. Some plugins help here (BaseElements comes to mind).

BTW: You should avoid trying to get a file path for managed files in container fields. For starters, you should avoid playing in managed folders. Second, important in server, those folder have rights and permissions designed for use by the server alone. External applications could have problems accessing files in those folders.

1 Like

Based on all the issues with managed files (stored externally, in an Open format), we decided to change our container field design and insteas store containers by reference only.

This has some advantages:

  • there is only 1 copy of the file on disk
  • if that file is updated outside of FileMaker, FileMaker will know about the newer version
  • this file can be treated like a "real" file which makes certain tasks (Sending Email with attachment) 1000x easier.
  • there are no worries with temporary files, files getting renamed (with _2 _3 _4...) and worry about deleting files or needing delay loops to do so.

The new problems:

  • If you drag & drop a file to a container field, it always inserts the file itself - there is no setting for the Table::Field or the layout Container Field to say "always insert by reference". This seems like a missing feature.
  • workarounds do exist: you can set the layout field to not be editable in Browse mode, which prevents Drag & Drop. However, this breaks the "double click container field to open file" feature. You can then add a dedicated button to open the file, but losing the drag & drop feature is too bad, and these workarounds feel inelegant.

No. Do not do that. You will break things. Those files belong to FileMaker not to the OS.

Sorry for the noise. I misread the post.

1 Like

I am confused. How do files inserted by reference belong to FileMaker? I was certain that Files inserted by reference are suppose to be managed by users at the OS level.

If you are going to have FileMaker use actual container fields, but want to Store by Reference, you will need to either:

  1. not allow drag and drop directly into the container (maybe make a drag-and-drop target in a web viewer?), or
  2. use a 3rd party plugin like (e.g. Troi File plugin), or
  3. not use Containers at all.

Webviewer idea (old post): Claris Community (English)
Troi File plugin: Drag a file into a FileMaker container field and store only a reference

Option #3 would be to store the PATH to a file in FileMaker (no drag-and-drop, unfortunately, unless you can get a web viewer to work as a drag-and-drop target), and then use a web viewer to show previews and other info about that external file, INSTEAD OF using a FileMaker Container field at all.
If you want "Store by Reference" behavior, that might be the better route.
Reason? That feature has been listed as "Deprecated" by Claris since FileMaker version 16.
See: Deprecated features in FileMaker 16
In other words, it IS still a feature in the product, but they are warning that it may go away in a future version of FileMaker. So, if you use it, you have to be aware that some day in the future, you are likely to either: a) not be able to upgrade FM, or b) have to replace it with some other method.

Given that, you could essentially build your own "Reference" feature by storing the path to to file, and using a web viewer to preview/view it, as well as the various "File" script steps in FM 18+ to manage it.

Huh, that's interesting, and... weird? I would have thought that the deprecations would be for FileMaker's way of storing fles internally. Storing files by reference seems like the more modern way to handle things to me.

you might confuse "external storage" setting with "Store by Reference". External Storage is as you earlier specified by using open storage (non-encrypted) kind of a "referenced" method. The deprecated "Store by Reference" is a different free style method where the file can be referenced on any path/drive you have access to but very easy to break because that path is user centric ...

2 Likes