Playing With The Zip Virtual File System (VFS) In Lucee 5.3.2.77
The other day, I was talking to fellow InVisioneer, Shawn Grigson, about how weirdly excited I am that Lucee has compress()
and extract()
functions. I don't know why; but, I just love how easy it is to create and consume Zip archive files. In response, Shawn reminded me that Lucee also supports the Zip virtual file system (VFS). Adobe ColdFusion has supported the virtual file system since CF10; but, I haven't really looked at it in a few years. And, I'm pretty sure I never used the Zip VFS. As such, I thought it would be fun to take a minute and try creating and consuming Zip files using the virtual file system in Lucee 5.3.2.77.
As a refresher, the Virtual File System (VFS) in ColdFusion provides a file-oriented abstraction for working with a variety of different resources. In this case, the VFS allows us to read from and write to Zip archive files just the same as if we were reading from and writing to a directory within our physical file system.
File paths in the Zip virtual file system have two parts: the physical part and the virtual part. The physical part defines the VFS schema, zip://
, and the path to our Zip archive file. The virtual part defines the path to our Zip entry within the archive file. The two parts of this VFS path are delimited with an exclamation point, !
(aka, bang):
zip://
+ physical_path + !
+ virtual_path
Given a path to a Zip VFS, we can read from and write to it the same way we would any other physical directory. Which means, we can use Lucee CFML functions like fileWrite()
, directoryCopy()
, and directoryList()
. To see this in action, I'm going to try creating and populating a Zip file using the virtual file system:
<cfscript>
// Let's check to make sure the Zip virtual file system is enabled before we attempt
// to use it to create a Zip archive file.
// --
// NOTE: I have this here simply as an exploration of the VFS metadata function.
if ( ! getVfsMetaData( "zip" ).enabled ) {
echo( "Zip VFS not enabled." );
abort;
}
// In the ZIP Virtual File System, the exclamation mark / bang symbol is used to
// delimit the "real path" (the location of the ZIP file) from the "virtual path"
// (the location of the Zip Entry within the Zip archive). The path following the
// "!" character will be relative to the root of the archive (even for absolute
// file paths, ie those that start with "/").
// --
// NOTE: Physical file paths in Lucee can be either absolute or relative to the
// current directory. As such, I can use "./" to define the file path relative to
// the working directory of the current CFML template.
zipFS = "zip://./archive.zip!";
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
fileWrite( "#zipFS#/README.md", "Welcome to my Archive" );
// Even though we're dealing with a "Virtual File System", the same rules around file
// IO still exist. We can't just write to a directory before it exists. As such, we
// have to ensure our sub-directories before we write to them (assuming that we're
// not using a function, like directoryCopy(), which can create parent paths).
directoryCreate(
path = "#zipFS#/files/poems/",
createPath = "true",
ignoreExists = true
);
// Write some content to the newly-created directory in our Zip archive.
fileWrite( "#zipFS#/files/poems/one.txt", "Roses are red, violets are blue..." );
fileWrite( "#zipFS#/files/poems/two.txt", "Once upon a time, in a land far away..." );
fileWrite( "#zipFS#/files/poems/three.txt", "There once was a man from Manhattan..." );
// Copy an entire directory.
// --
// NOTE: Even though the "haikus" directory doesn't exist in our Zip, the
// directoryCopy() function is capable of creating the destination path via the
// "createPath" option. As such, we don't need to ensure directory before we write
// to it.
directoryCopy(
source = "./haikus/",
destination = "#zipFS#/files/haikus/",
recurse = true,
createPath = true
);
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// Now that we've create and populated our Zip archive with files, let's look at what
// we have in our virtual file system.
fileList = directoryList(
path = zipFS,
recurse = true
);
// The directory list of the Zip archive will return file-paths that are prefixed
// with the location of the archive file. As such, let's strip off that prefix for
// simpler rendering. We can do easily when we remember that the exclamation point
// ("!") delimits the physical file path from the virtual file path.
shortFileList = fileList.map(
( path ) => {
// Return only the "virtual file path" portion of the full file path.
return( path.listRest( "!" ) );
}
);
dump(
label = "Zip Virtual File System",
var = shortFileList
);
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
// As a final step in the demo, let's extract the Zip archive so we can examine the
// contents in the Finder.
extractPath = "./extract-#createUniqueId()#/";
directoryCreate( extractPath );
extract( "zip", "./archive.zip", extractPath );
</cfscript>
As you can see, this demo works by creating a Path variable (zipFS
) that points to a directory within the Zip virtual file system. We then use this variable to prefix the path of all of the subsequent File IO operations. And when we run this CFML code, we get the following output:
As you can see, we were able to treat our Zip archive file just like any other directory on the physical file system! Very cool!
I don't really know why I'm so enamored with Zip archive files. Maybe it has something to do with how easy they make exporting and packaging data. Regardless, I do appreciate how little effort it takes to create Zip files in Lucee CFML. The Zip Virtual File System is not the only way to do it - but, it's certainly a fun way to do it.
Want to use code from this post? Check out the license.
Reader Comments
So you could mount a docx file this way I guess!
@Zac,
Oooh, very interesting. Is a
docx
file basically like a glorified archive file? That would be fun to try!Ben, I am trying to find details on how to use the cfzip tag in cfscript under Lucee. I was assuming I could just use it as a function:
cfzip( ... )
. But Lucee 5.3.6 is sayingNo matching function [CFZIP] found
. I can't find any examples.@Christopher,
Yes, all you have to do is remove the
cf
part, and treat it like a script-based tag:zip { }
. I have a bunch of examples regarding Lucee CFML here:www.bennadel.com/blog/3767-using-coldfusion-tags-in-cfscript-in-lucee-cfml.htm
Hope that helps!