Virtual File Systems
Virtual File Systems
Lucee uses virtual file systems for all file operations. Actions like fileRead(file) work against any defined filesystem. Each VFS uses a protocol prefix (http://, s3://, etc.). Without a prefix, the default file:// (local filesystem) is used.
Built-in:
- file (default)
- ftp
- http / https
- ram
- zip / tgz / tar
Extensions:
- git
- S3
File (Local)
Default filesystem. Example:
<cfscript>
sct.file = getCurrentTemplatePath();
sct.directory = getDirectoryFromPath(sct.file);
dump(sct);
dump(fileRead(sct.file));
dump(directoryList(sct.directory));
</cfscript>
getCurrentTemplatePathreturns the current template pathgetDirectoryFromPathreturns the directoryfileReadreads the content of a filedirectoryListlists all entries in a directory
Explicit file:// prefix is optional:
<cfscript>
sct.file = getCurrentTemplatePath();
sct.directory = getDirectoryFromPath(sct.file);
dump(sct);
sct.file = "file://" & sct.file;
sct.directory = "file://" & sct.directory;
dump(sct);
dump(fileRead(sct.file));
dump(directoryList(sct.directory));
</cfscript>
Pattern
[file://]<path> e.g. file:///Users/susi/Projects/local/file.txt or /Users/susi/Projects/local/file.txt
FTP
Treat remote FTP servers as a filesystem. Configure credentials in Application.cfc:
// How to configure default FTP settings via Application.cfc
this.ftp.username = "secretUser";
this.ftp.password = "secretPW";
this.ftp.host = "ftp.lucee.org";
this.ftp.port = 21;
Then use:
dir = directoryList("ftp:///dir/file.txt");
dump(dir);
Or include credentials in the path:
<cfscript>
dir = directoryList("ftp://secretUser:secretPW@ftp.lucee.org:21/dir/file.txt");
dump(dir);
</cfscript>
Mix partial credentials from Application.cfc with path values - path values override Application.cfc.
Pattern
ftp://[{user}:{password}@][{host}][:{port}]/{path} e.g. ftp://secretUser:secretPW@ftp.lucee.org:21/file.txt
HTTP/HTTPS
Read-only filesystem for get and head calls. No directoryList() support. Authentication can be given in the path or per host in the Application.cfc (Lucee 9.0+, see LDEV-3376).
<cfscript>
dump(fileRead("https://lucee.org/index.cfm"));
</cfscript>
Credentials
Always use https:// when you send credentials, with http:// they travel unencrypted.
Credentials in Path
Sends the credentials as HTTP Basic authentication. Less secure - credentials may end up in logs or exceptions.
content = fileRead("https://secretUser:secretPW@example.org/protected/file.txt");
Application.cfc
Define the authentication per host with this.vfs.http. The key is the host, optionally with a port (host:port). An entry for host:port is used before an entry for host. Use bracket notation for the keys, as a host name contains dots.
// HTTP Basic authentication
this.vfs.http["example.org"].username = "secretUser";
this.vfs.http["example.org"].password = "secretPW";
// arbitrary request headers, e.g. a bearer token or an API key
this.vfs.http["api.example.org:8443"].headers = {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiJ9.EXAMPLE",
"X-Api-Key": "secretKey"
};
<div class="highlight"><pre><span></span>
</pre></div>
<br>lucee
content = fileRead("https://example.org/protected/file.txt");
content = fileRead("https://api.example.org:8443/data.json");
| Key | Description |
|---|---|
username |
User name for HTTP Basic authentication. |
password |
Password for HTTP Basic authentication. |
headers |
Struct of request headers sent with every request to the host. Values must be simple values. |
Rules:
usernameandpasswordare sent with the first request (preemptive), you do not need to wait for a401challenge.- If
headerscontains anAuthorizationheader, it is used instead ofusernameandpassword. - Credentials in the path override the authentication of the
Application.cfcfor that request. Other headers defined inheadersare still sent. - Headers and credentials are only sent to the host they are defined for. A redirect (
301,302,303,307,308) to another host, port or protocol is followed without them. - An entry that is not a struct, or
headersthat is not a struct, throws an exception.
Pattern
https://[{user}:{password}@]{host}[:{port}]/{path} e.g. https://lucee.org/index.cfm
RAM/Cache
Treat JVM memory as a filesystem. Fast for temporary files, but data lost on restart (unless backed by cache). RAM filesystem is server/instance-wide - all requests on that Lucee instance share it.
Note: With modern SSDs, the performance difference between RAM and local file operations may be negligible for many use cases.
<cfscript>
sct.ram = "ram://";
dump(sct);
dump(directoryCreate(sct.ram & "/heidi/"));
fileWrite(sct.ram & "susi.txt", "sorglos");
dump(directoryList(sct.ram));
</cfscript>
Cache Backing
Back RAM filesystem with a cache for persistence and distribution across servers.
In Lucee Administrator: Services > Cache, define a cache (EHCache, Redis, Couchbase, etc.) then set as default for "Resource".
Or in Application.cfc:
// link a cache to be used as resource cache
this.cache.resource = "cache"; // name of the cache
This setting is per application, so each application can back ram:// with its own cache. Without it, the default "Resource" cache from the Administrator is used, or a built-in RAM cache if none is set.
this.inMemoryFileSystem is accepted as an alias that takes a cache name:
this.inMemoryFileSystem = "cache"; // same as this.cache.resource = "cache"
The alias is only used when no resource, http, file or webservice default cache is set via this.cache.
There is no separate size setting for the RAM filesystem. Size limits and eviction come from the configuration of the backing cache.
Pattern
ram:///{path} e.g. ram:///path/to/my/file.txt
ZIP/TGZ/TAR
Access compressed files as filesystems using zip://, tgz://, or tar:// prefix.
<cfscript>
sct.zip = "zip://path/to/the/zip/test.zip!/path/inside/the/zip/";
dump(directoryList(sct.zip));
dump(fileRead(sct.zip & "/file.txt"));
</cfscript>
Pattern
zip://{path-zip-file}!/{path-inside-zip}tgz://{path-tgz-file}!/{path-inside-tgz}tar://{path-tar-file}!/{path-inside-tar}
e.g. zip://path/to/the/zip/test.zip!/path/inside/the/zip/file.txt
S3/Object Storage
Remote filesystem for Amazon S3 and compatible object storage providers:
- Amazon S3
- MinIO
- Wasabi
- Backblaze B2
- Google Cloud Storage
- Microsoft Azure Blob Storage
- IBM Cloud Object Storage
- DigitalOcean Spaces
- Ceph
- Alibaba Cloud OSS
- DreamHost DreamObjects
- Scality RING
- Dell EMC ECS
- Cloudian HyperStore
- OpenIO
- NetApp StorageGRID
Uses s3:// prefix (originally for Amazon S3, now works with all compatible providers).
Credentials
Environment Variables / System Properties
LUCEE_S3_ACCESSKEYID: AKIAIOSFODNN7EXAMPLE
LUCEE_S3_SECRETACCESSKEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# optional
LUCEE_S3_HOST: s3.eu-central-1.wasabisys.com
LUCEE_S3_REGION: eu-central-1
LUCEE_S3_ACL: public-read
Or system properties: -Dlucee.s3.accesskeyid, -Dlucee.s3.secretaccesskey, -Dlucee.s3.host, -Dlucee.s3.region, -Dlucee.s3.acl
Application.cfc
Single credential set:
this.s3.accessKeyId = "AKIAIOSFODNN7EXAMPLE";
this.s3.secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
// optional
this.s3.host = "s3.eu-central-1.wasabisys.com"; // not needed for AWS
this.s3.defaultLocation = "eu-central-1";
this.s3.acl = "public-read";
Multiple named credential sets:
// my wasabi
this.vfs.s3.mywasabi.accessKeyId = "AKIAIOSFODNN7EXAMPLE";
this.vfs.s3.mywasabi.secretKey = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY";
this.vfs.s3.mywasabi.host = "s3.eu-central-1.wasabisys.com"; // not needed for AWS
// my aws
this.vfs.s3.myaws.accessKeyId = "AKFHJUKHZZHEXAMPLE";
this.vfs.s3.myaws.secretKey = "sdszhHJNkliomi/K7MDENG/bPxRfiCYEXAMPLEKEY";
Use named credentials:
dir = directoryList("s3://mywasabi@/path/inside/wasabi.txt");
Credentials in Path
Less secure - credentials may be exposed in exceptions (Lucee 6+ suppresses this, but still risky).
With credentials from environment/Application.cfc:
dir = directoryList("s3:///mybucketName/myObjectFolder/myObject.txt");
With named credentials:
dir = directoryList("s3://mywasabi@/mybucketName/myObjectFolder/myObject.txt");
Full credentials in path:
dir = directoryList("s3://AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY@s3.eu-central-1.wasabisys.com/mybucketName/myObjectFolder/myObject.txt");
dump(dir);
Pattern
s3://[{access-key-id}:{secret-access} || {name}]@[{host}]/{path-inside-s3}
e.g. s3://AKIAIOSFODNN7EXAMPLE:wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY@s3.eu-central-1.wasabisys.com/mybucketName/myObjectFolder/myObject.txt
GIT
Access Git repositories as a filesystem - map webroot directly to a repo. Files cached locally, changes auto-detected. Read-only.
Credentials
Environment Variables / System Properties
LUCEE_GIT_USERNAME: whatever
LUCEE_GIT_PASSWORD: qwerty
Or system properties: -Dlucee.git.username, -Dlucee.git.password
Application.cfc
this.git.username = "whatever";
this.git.password = "qwerty";
this.git.repository = "https://github.com/lucee/lucee-examples";
this.git.branch = "master";
Usage:
dir = directoryList("git:///path/inside/repo");
Credentials can't be in the path (security), but branch and repository can:
dir = directoryList("git://master@github.com/lucee/lucee-examples!/path/inside/repo");
dump(dir);
Pattern
git://[{branch}@][{repository}]!/{path-inside-repo} e.g. git://master@github.com/lucee/lucee-examples!/path/inside/repo