Container Data and the FileMaker Data API

Back when I was learning about the FileMaker Data API, I had trouble finding any information on how container data was handled. Oddly, the upload information in the Help documentation seemed far more complete than the download information, especially considering the download information isn’t even it’s own section. It’s a note in the URL And Data Format notes section. In essence, the note is: You will get a URL referencing the location of the container data, which is somewhere on the internet. You’re welcome. I ended up utilizing the Base64 Encode And Decode functions in a way that I’ll forever keep as a tool in my developer’s toolbox. If you’re not familiar with these functions, they easily convert images into text and vice versa.

My first project utilizing the FileMaker Data API was converting one of my own two-file sync systems to a single-file API-based sync system. The original configuration looked deceptively simple:

The mobile file is only connected to server whenever we open up the Sync file, and even then it is via relationship rather than external file reference.

There are a couple of problems with this. The main one is that you have two files to maintain and distribute. Across a few users, that’s manageable. Across 20 or more, it’s rather clunky. Beyond that, there are a lot of moving parts, causing one’s hair to fall out. I, for one, do not need less hair.

The conversion to sync via API was made slightly easier because I wasn’t addressing all the tables in the system. I was only needing to send and receive data between a couple of tables. On the mobile side, there is a MobileEditLog table that captures all of the changes made on mobile. On the server side, there is a place to dump these logs and process them so that it updates server. On the server side, there is a ServerEditLog table that serves the same purpose of capturing changes that happen in the office with desktop users. On the mobile side, there is a place to dump these logs and process them so mobile data gets updated. I only needed those two tables to talk to each other.

In this scenario, the Mobile edit log should always match on both sides for a given mobile user.

You know that place where you have to learn that new thing before you can finish the time-critical piece of the project you’re working on, but there seems to be no help anywhere on the internet? Well, that’s where I found myself. All I had left to do was the moving of images back and forth between mobile and server. Specifically, I needed to capture signatures in mobile and send them to server. Later, if we downloaded a signed form to the iPad, I would need to bring those signatures back to be able to display them in the user interface.

Naturally, long after I was done, I finally found what I was looking for in the Claris Community Forum. It was a self-answered question by Sky Willmott who was trying to figure out the download process as I was. It would have been lovely to stand on Sky’s shoulders and take the glory for being the hero simply for copying someone else’s work, but alas, I had to think for myself, and fast.

The MobileEditLog and the ServerEditLog tables are field-level change logs. When a user clicks into a field, the current state of that field is saved in a global variable, and when the user leaves the field, its current value is compared to the value in that global variable. If there is a difference, an edit log is generated. Each record represents the before and after values for one field.

The upload part was easier so I worked on that first. The general idea was to create a record and then edit it with the image.

Notice in the screenshot of the upload routine on line 18, I started with sending the log record to the server. When creating a record using the FileMaker Data API, the record’s ID is returned in the result. In line 27, I grab that result and store it in the variable $RecordID.

Upload routine

 Then I build a new API call on line 34 that looks like this:

 

https://xx.xxx.xx.xxx/fmi/data/v1/databases/MyFile/layouts/Sync Uploads/records/72796/containers/Image/1

…with these curl options:

“–show-error ” &

“–dump-header $$Headers ” &

“-X POST ” &

“–header \”Content-Type:multipart/form-data\” ” &

“–header \”Authorization: Bearer ” & $$Token & “\”” &

” -F \”upload=@$Image;filename=” & $ImageName & “\””

 

The cURL statement includes “-F” which stands for form. According to this site:

[-F] enables uploading of binary files etc. To force the ‘content’ part to be a file, prefix the file name with an @ sign. To just get the content part from a file, prefix the file name with the symbol <. The difference between @ and < is then that @ makes a file get attached in the post as a file upload, while the < makes a text field and just get the contents for that text field from a file.

This worked perfectly and solved half of my problem. Note that this method addresses one record at a time, and does not account for trying to send bulk images in a JSON blob. Even at the times where I have multiple images to send, I simply loop through them and use this code to handle them one at a time. And for this system, that works just fine.

Downloading a record was easy. But downloading the image in the container field of that record was a whole different story. In this case, we might be downloading a previously-signed form to FileMaker Go so we can look over the details. This means being able to download the signatures so that the user can see who signed them.

When you retrieve the record via the API, you get some JSON that contains a URL representing the image. Sky Willmott posted this JSON to demonstrate what the returned container data URL looks like:

{

  “data”: [

    {

      “fieldData”: {

        “myContainerField”: “https://myserver.com/Streaming_SSL/MainDB/58F59F18526716ED0D1CEB2A7B5BD2A9BD9F528DEC56A828F6F0C6FB4653C785?RCType=EmbeddedRCFileProcessor https://myserver.com/Streaming_SSL/MainDB/58F59F18526716ED0D1CEB2A7B5BD2A9BD9F528DEC56A828F6F0C6FB4653C785?RCType=EmbeddedRCFileProcessor “

      },

      “modId”: “45”,

      “portalData”: {},

      “recordId”: “3”

    }

  ],

  “errorCode”: “0”,

  “result”: “OK”

}

In his forum post, he tells how to get the image using that URL. But because I didn’t find his wonderful instructions in time, I was forced to figure it out on my own. What I knew for certain was that an image would have that “?RCType=” argument in it (see bold text in the JSON above). If I encountered that string inside of the URL as I processed a downloaded record, I stopped and made an API call to grab the image, like so:

Grabbing the image via the FileMaker Data API

My goal was to call a script on server, as seen in line 66. Running a script on server would allow me to run whatever FileMaker steps I needed to to find the image, package it with Base64 Encode, and handle errors in a way that I could track down if anything went wrong. Once encoded, the image was sent back as a script result.

To do that, I would need to tell Server which record I was looking for. I used the script parameter argument in the url to accomplish that. I set the parameter to these name-value pairs (using the old Six-Fried Rice parameter passing scheme at the time):

Base64Encode (  # ( “ID” ; $ID ) &

                            # ( “LayoutName” ; Get ( LayoutName ) ) &

                                        # ( “FieldName” ; $FieldName )

                                      )

On the server side, the script decodes the Base64 and performs a find for the passed ID on the layout in the parameter. When it finds that record, it Base64-encodes the image and sends it back via the $Result variable in the Exit Script step.

Line 30’s variable is built so that we grab both the image in Base64 and the image’s extension:

Base64Encode ( GetField ( Get ( LayoutTableName ) & “::” & $FieldName ) )

&

# ( “ext” ; GetField ( Get ( LayoutTableName ) & “::” & $FieldName ) )

Notice FileMaker’s behavior here. If we Base64-encode the field, we get the image. If we ask for it as text, we get the name of the image. Very handy.

Once we have the script result from the API, we parse out the image and the image extension based on the name:

Parsing the script result and setting the image

In this particular version, I’m not actually decoding the name of the image because it’s not critical. All I’m doing is giving it an arbitrary name, and in this case it’s the field name of “Image”. So every image that’s downloaded will have the name “Image.png”, or whatever the extension happens to be. But as long as the extension is correct, it works.

(If we needed the actual name of the image, I would parse that out from the “ext” parameter. The decision there was that I didn’t want any strange characters in image names messing up the downloads. I just wanted the downloads to work.)

Using Base64 to move data around through the URL as a parameter is a very handy trick, as it allows you to build whatever payload you want, in whatever format you want. When you decode it at the other side, it’s exactly as you sent it.

So that took care of my need to be able to download images. It has worked perfectly for a few years now out in the field, so I couldn’t be happier with it. In the end, I gained a nice tool for packaging up not just images, but any data set I wish on the fly.

Hopefully this helps you, too. Let us know!

1 thought on “Container Data and the FileMaker Data API”

  1. Thanks for the nod towards my Community post in your write up here 🙂
    Interesting blog post and I’m looking forward to reading some others here…
    Sky Willmott

Leave a Reply

Your email address will not be published. Required fields are marked *