In FileMaker, container fields are super handy. Combined with some database structure, they make it easy to associate multiple stored files with a single record. Even better, container fields are supported in WebDirect.
The Use Case
Say you have several container fields associated with a single record and you want to get all those related files at once. You would like it in one nice, convenient zip file that automatically downloads when you click a button. In the standard interface, you would need to download each file individually. We can do better.
By combining a little Custom Web Publishing (CWP) with WebDirect, we can extend the capabilities and provide some added functionality for our users.
Using the FileMaker API for PHP, we can retrieve all records with container data, zip them all in one archive and automatically have the users web browser download it.
You know, “I don’t want to click on a bunch of things, I want my stuff now…” 🙂
How It Works
We set the URL of a web viewer in WebDirect, so we can pass a record ID to our PHP script. This enables us to find the correct record in FileMaker from our PHP script. No problem there.
Now the interesting part. PHP already has built in zip functions, see http://php.net/manual/en/book.zip.php. All we need to do is feed it some data. To get at the container data, we take advantage of the Base 64 functions in FileMaker. There are two parts to each file, the contents of the file itself, which we get in base 64, and the file name. Both of these are something we can calculate from container data.
First, we instantiate a zip archive in the temp directory on the server. Then we loop through the found records and add them all to the zip archive, since there can potentially be multiple related records for the parent record. PHP has a function to decode the base 64 data (base64_decode) that we use to insert the data in the archive.
// create the zip archive
$zip = new ZipArchive();
$zipname = 'records_' . date('U');
$filename = '/tmp/' . $zipname . '.zip';
if ($zip->open($filename, ZipArchive::CREATE) !== TRUE) {
exit("cannot open <$filename>n");
}
foreach ($fm_records as $value) {
$the_file = base64_decode($value->getField('File_b64'));
$the_file_name = $value->getField('File_name');
$zip->addFromString($the_file_name, $the_file);
}
$zip->close();
That is all that is needed, once we finish our foreach loop, all the data is now in our zip file. In this sample, I have a rather high limit of 1000 records found, which can be adjusted as needed, but should be sufficient for most uses.
Auto Download
Using PHP, we can adjust the HTTP headers to tell the users web browser to start automatically downloading the zip file. That way, there is no link to click on…no need to right click and do a “Save As…” in order to download the file. What makes this work, most importantly, are these two headers:
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . ""$file_name"");
The variable “$file_name” is set earlier in the PHP script, so can be whatever we want. In this case, it is a generic “records_” and a date string to be somewhat unique, but it can be anything we like.
After the download is finished, we unlink the file in our PHP script, which removes the temporary file on the server. It is always good to clean up after yourself, if possible.
By extending the capabilities of WebDirect, there are more possible uses to apply it to. Additionally, providing a more convenient and intuitive user experience is well worth the effort. Those who use your application will appreciate it.
Get the files from GitHub:
Note: Click the “Download Zip” link at the bottom of the right-hand column.
A Few Other Notes:
This example is meant to be open and easy enough to follow. If security is a concern, that could be addressed with some additional consideration.
Also, if SSL is being used in WebDirect, then of course the PHP script location should as well. If WebDirect’s URL begins with “HTTPS” then the PHP script should also be called from “HTTPS.”
References
- FileMaker WebDirect – FileMaker
- PHP Zip Documentation – php.net
- FileMaker 13 – Working with Base 64 – Soliant Blog
This is an excellent little utility which I’ll have great fun with in future, I’m sure. However, I’ve run across a small issue which has got me absolutely baffled:
I adapted your PHP and FM scripts to use in a small test database I have running on a demo of FileMaker 14 server. When I insert images into the files (child) table using the Insert file menu method, your script works with no issues. However, when I use the Insert File script step to upload five or six images at one time from FM Pro 13 Advanced, everything at first appears to have worked OK. The images are in the containers and the filenames are recorded. The images also show in a portal on the parent table.
However, the PHP script, when called from a WebDirect session, just doesn’t work … it appears that the call in the FileMaker API is not finding any child records, despite the correct ID being passed the PHP script parameter. Have you any ideas why this might be happening?
BTW, I noticed that your “Save Zip File” creates a URL variable with has a double forward slash in the path. Is there a reason for this? It doesn’t seem to make any difference if I change it to a single slash …
Hi Ian,
Could it be a cross domain issue where the php script and the webdirect session are accessed with different protocols? You might have difficulty mixing http and https urls, so you will want them to be the same when using webdirect. You can also use FM Pro Advanced to troubleshoot what URL is being called and check it in a web browser like safari or chrome.
Mike
Mike:
I’m pretty sure the problem is not related to the URL, because in all cases in my example database I’ve referred to http://localhost/.
However, I do think I’ve tracked down the issue. It seems that a few years ago PHP’s libxml introduced a hardcoded 10MB limit on any XML data returned.
See https://bugs.php.net/bug.php?id=49660 and http://forums.filemaker.com/posts/8742de6857?commentId=323487#323487.
The error reported in the second link is exactly the one I get when I try to create a zip file which contains data larger that 10MB.
Reading TSGal’s reply gives no indication when we can expect a fix, but this issue is severely limiting FileMaker’s PHP API. After all, if we can’t return more than 10MB of data from FileMaker server, then what’s the point?
Cheers
Ian
Pingback: FileMaker DevCon: Day 0 – FMT Network
Hello Mike,
first of all thanks for all the great examples!
I am trying to use this functionality with the webdirect but no matter what I do I get a server 500 internal error. I am trying with a windows server 2016 with the latest version of Filemaker Server 17, php is on and working (I tested with the demo database). I have tried with and without SSL certificate but no luck at all.
Is there something I am missing?
Thanks
PHP 500 errors on Windows servers are very generic, even if you specify to show errors. It is much easier to troubleshoot the PHP errors on apache servers. My guess is it could be a memory error. Try it with smaller files first to test, and there may be options for increasing the default memory for PHP to run on the server.