
1. Working with ZIP files
Since version 1.7.0.20 - the ACF-plugin has now extended its filesystem functions to also work with files inside ZIP archives. Two new functions have been added:
- open_zip (string zip-file-path) - returning a prefix string to be applied to all file paths inside the archive.
- close_zip (zip prefix) closes the zip archive.
The open-zip opens the zip archive and returns a short prefix string to be in front of file paths inside the archive. The prefix string is in the form: ZIP<0>:
where the zero can be any number or digits. this string is returned from open_zip
and should never be tampered with.
All the other file-system functions now work on this new prefix system. If you open a file with this prefix in the path, it will open it in the archive.
Ensure that you use the close_zip
function, especially after you have done some modifications to the archive. Otherwise, the archive or the files you added/modified might get corrupted.
The following file-system functions work with both zip paths and regular OS paths.
- open
- close
- read - read the content of a file, whether in a Zip or the OS file system.
- write - write to the content of a file, whether in a Zip or the OS file system.
- copy_file - copy between zip files, between os-files and zip in either direction. It is also for directories.
- move_file - move between zip files, between os-files and zip in either direction. It is also for directories.
- list_files
- file_exists
- directory_exists
- Create_directory - directories are automatically created in zip files, so this is not needed for zip-file paths. Just open a file in the directory, and it will be created.
- delete_file
- delete_directory
The markdown functions are not sensitive for zip paths, but the result can be put in a zip file with copy_file afterward.
Note: The filesystem inside ZIP archives has some different properties from the OS filesystem. The files are not organized in folders in the same way. It is just a list of files complete, with directory separators /
- like the full path is part of the filename. There are no directory entries in the zip archive. Therefore, functions like directory_exists
loop through the file list to find any matches. list_files
function uses the path in the argument to filter the list like every file is listed that starts with the path given.
Note: copy_file
and move_file
can copy files and folders. If copying to or from the zip archive and the OS filesystem, the path in the zip archive is defined as a folder when it ends with a slash /
If the destination is a folder, the files copied will be given the name of the source name.
Examples:
The example below, using the following FileMaker script triggered by a button:
function test_zip_archive (string archive, string filename, string content)
int x, y, z;
string path, xcontent;
// Open the ZIP archive
string zip = open_zip ( archive );
// Check if we got a prefix
if ( left (zip, 3) == "ZIP") then
// open two files in the arcive
x = open ( zip + filename, "w") ;
y = open ( zip + "ZipDirectory/Mytestfile6.txt", "w");
// Write some content to them .
write (x, content);
write (y, "content into file6");
write (x, "\nSome more content in line two...");
write (y, "\nSome more content in line two into file6");
// close the files.
close (x);
close (y);
// Ask for a third file to include in it.
path = select_file ( "Select a file to include");
// If the user did not hit cancel, but selected a file:
if ( path != "") then
z = open ( path, "r"); // Regular OS file
xcontent = read (z);
path = Get_FilenameFromPath(path);
close (z);
// Write the content to a file in the archive,
z = open ( zip + "ZipDirectory/"+path, "w");
write (z, xcontent);
close (z);
end if
close_zip(zip);
else
alert ( zip );
end if
return "OK";
end
Here is the ZIP file, and unpacked directory to show the files we made.
1.1. Some Use cases ↑
- If you generate several files like some reports, it can be good to pack the files into a ZIP archive to easily have them grouped as belonging to the same run.
- The EXCEL format (.xlsx) is a zip file. If you need to modify, create, or read data from it, you can do so by opening the file as a zip archive and working with the files in it. Using the XML functions on the XML files in it is also possible.
- Some web services expect files to be transferred as a ZIP archive. You can automate this process by preparing it and sending it to the web service or retrieving it from the service and working with it directly in an ACF function.
1.2. Working with EXCEL .xlsx files from FileMaker ↑
As mentioned above, EXCEL spreadsheet files in the .xlsx format are zip archives. This means that we can use the zip functionality described here to open those files directly and work with the content.
if we use the list_files
function on such an EXCEL file, like this ACF code:
string path = "~/Desktop/MyExcelFile.xlsx";
string zip = open_zip ( path );
print list_files(zip);
close_zip (zip);
This results in the following list:
[Content_Types].xml
_rels/.rels
xl/_rels/workbook.xml.rels
xl/workbook.xml
xl/sharedStrings.xml
xl/styles.xml
xl/worksheets/sheet1.xml
xl/theme/theme1.xml
docProps/app.xml
docProps/core.xml
xl/calcChain.xml
Now, By looking at the spreadsheet, there are some numbers we want to extract from this sheet.
After a little digging - the file xl/worksheets/sheet1.xml
contains what we are looking for. The data is in a structure like this (a little simplified):
<worksheet>
...
<sheetData>
...
<row>
<c><v>some value</v></c>
<c><v>some value</v></c>
<c><v>some value</v></c>
<c><v>some value</v></c>
</row>
<row>
<c><v>some value</v></c>
<c><v>some value</v></c>
<c><v>some value</v></c>
<c><v>some value</v></c>
</row>
...
etc
We want Row 5 to Row 9, Column 5
Then the following snippet does exactly that:
string path = "~/Desktop/MyExcelFile.xlsx";
int x, i;
string content;
XML sheet1;
// Open EXCEL file
string zip = open_zip ( path );
// Retrieve the sheet data
x = open ( zip + "xl/worksheets/sheet1.xml","r");
content = read (x ) ;
close (x);
close_zip (zip);
// parse XML
sheet1 = content;
// Extract the content into an array:
ARRAY STRING colE5_9;
for (i=5,9 )
colE5_9[] = string(sheet1["worksheet.sheetData.row["+i+"].c[5].v"]);
end for
// Return as comma separated values.
return implode (",", colE5_9);
Running this function, resulted in the following:
6300,239519,74335,14500,108521
This is exactly what we can see in the spreadsheet.