Accessing CDNs in Your Own Code

There are situations where you will want to access the CDN storage points yourself for custom functionality.

A couple of cases where we've done so:

  • automatically generating archvies and uploading them to S3
  • writing a migration utility to compare two CDN locations and copy selected files between them
  • clearing out a CDN after doing tests on stage

This section describes the public API of the CDN library, which takes care of things like ensuring filename uniqueness, figuring out how to move files between different CDNs, and handling user uploads. Note that ALL public functions are prefixed with "io" to avoid conflicts with existing CFML functions.

You can access these function at application.fc.lib.cdn.

ioFileExists

ArgumentDescription
location REQThe location to check.
file REQThe file to check.

Does what it says on the box. Checks a single location to see if a file exists.

Example
<cfif application.fc.lib.cdn.ioFileExists(location="cache",file=sCacheFileName)>
    <cfreturn sCacheFileName />
</cfif>

ioFindFile

ArgumentDescription
locations REQA list of locations to search.
file REQThe file to find.

Searches the provided locations, and returns the first that contains the specified file, or an empty string if there isn't any.

Example
<cfset currentLocation = application.fc.lib.cdn.ioFindFile(locations="privatefiles,publicfiles",file=arguments.stObject[arguments.stMetadata.name]) />

ioGetUniqueFilename

ArgumentDescription
locations REQA list of locations that the filename needs to be unique among.
file REQThe file to update.

Returns a version of the specified filename which is unique among every listed location by appending numbers to the name. Note that it is rare to have to call this function directly, as all functions which put a file into a CDN have options to enforce filename uniqueness. However if you wish to change how FarCry enforces uniqueness, you can override this function in your project.

Example
<cfset moveto = ioGetUniqueFilename(locations="privatefiles,publicfiles",file=newfile) />

ioGetFileSize

ArgumentDescription
location REQThe location to check.
file REQThe file to inspect.

Returns the size of the file in bytes.

Example
<cfoutput>Size: <span class="image-size">#round(application.fc.lib.cdn.ioGetFileSize(location="images",file=arguments.stMetadata.value)/1024)#</span>KB</cfoutput>

ioGetFileLocation

ArgumentDescription
location REQThe location to check.
file REQThe file to return.

Returns serving information for the file. The result is a struct - either: method=redirect + path (URL) OR method=stream + path (local path).

Example
<cfset stImage = application.fc.lib.cdn.ioGetFileLocation(location="images",file=arguments.stMetadata.value) />
<cfoutput><img src="#stImage.path#"></cfoutput>

ioWriteFile

ArgumentDescription
location REQThe location to write to.
file REQThe file to write to.
data REQThe data to write to file.
datatype OPTOne of text, binary, and image. Defaults to text.
quality OPTThis is only required for JPEG image writes. Defaults to 1.
nameconflict OPTOne of makeunique and overwrite. Default is overwrite.
uniqueamong OPTIn the case of makeunique, this is the list of locations that the file should be unique in. Defaults to the same as location.

Writes the specified data to a file.

Example
<cfset stResult.filename = application.fc.lib.cdn.ioWriteFile(location="images",file=filename,data=newImage,datatype="image",quality=arguments.quality,nameconflict="makeunique",uniqueamong="images") />

ioReadFile

ArgumentDescription
location REQThe location to check.
file REQThe file to read.
datatype OPTOne of text, binary, and image. Defaults to text.

Reads from the specified file.

Example
<cfimage action="info" source="#application.fc.lib.cdn.ioReadFile(location='images',file=stResult.value,datatype='image')#" structName="stImage" />

ioMoveFile

ArgumentDescription
source_location OPTThe location to move the file from.
source_file OPTThe file to move.
source_localpath OPTThe local file to move.
dest_location OPTThe location to move to.
dest_file OPTThe file to move to. Defaults to the same as source_file.
dest_localpath OPTThe local file to move to.
nameconflict OPTOne of makeunique and overwrite. Default is overwrite.
uniqueamong OPTIn the case of makeunique, this is the list of locations that the file should be unique in. Defaults to the same as location.

Moves the specified file between locations. Note that when moving a file between different CDN types, this function moves the file to the local temporary directory, then to the target location from there.

Note that while every argument is marked as optional, in practice you need:

  • source_location and source_file OR source_localpath
  • dest_location and dest_file OR dest_localpath
Example
<cfset application.fc.lib.cdn.ioMoveFile(source_location="publicfiles",source_file=arguments.stObject[arguments.stMetadata.name],dest_location="privatefiles") />

ioCopyFile

ArgumentDescription
source_location OPTThe location to copy the file from.
source_file OPTThe file to copy.
source_localpath OPTThe local file to copy.
dest_location OPTThe location to copy to.
dest_file OPTThe file to copy to. Defaults to the same as source_file.
dest_localpath OPTThe local file to copy to.
nameconflict OPTOne of makeunique and overwrite. Default is overwrite.
uniqueamong OPTIn the case of makeunique, this is the list of locations that the file should be unique in. Defaults to the same as location.

Copies the specified file between locations. Note that when copying a file between different CDN types, this function copies the file to the local temporary directory, then moves it to the target location from there.

Note that while every argument is marked as optional, in practice you need:

  • source_location and source_file OR source_localpath
  • dest_location and dest_file OR dest_localpath
Example
<cfset application.fc.lib.cdn.ioCopyFile(
    source_location="images",
    source_file=stLocal.stInstance.thumbnail,
    dest_location="archive",
    dest_file="/#stLocal.stInstance.typename#/#stLocal.stProps.archiveID#_thumb.#ListLast(stLocal.stInstance.thumbnail,'.')#"
) />

ioUploadFile

ArgumentDescription
location REQThe location to upload to.
destination REQThe target location. This can be a directory (in which case the filename from the upload is used) or a specific filename. Note that if a specific filename is provided, the function will check that the upload has the same extension and throw an uploaderror if it doesn't match.
filed REQThe form post field.
nameconflict OPTOne of makeunique and overwrite. Default is overwrite.
uniqueamong OPTIn the case of makeunique, this is the list of locations that the file should be unique in. Defaults to the same as location.
acceptextensions OPTIf this is provided, then the function will check the uploaded file extension. If the extension is invalid an uploaderror will be thrown.
sizeLimit OPTIf this is provided, then the function will check the size of the uploaded file. If the size is too great an uploaderror will be thrown.

Uploads the a file to the specified location.

Example
<cfset stResult.value = application.fc.lib.cdn.ioUploadFile(
    location="securefiles",
    destination=arguments.stMetadata.ftDestination,
    field="#stMetadata.FormFieldPrefix##stMetadata.Name#New",
    nameconflict="makeunique",
    uniqueamong="privatefiles,publicfiles",
    acceptedextensions=arguments.stMetadata.ftAllowedFileExtensions
) />

ioDeleteFile

ArgumentDescription
location REQThe location to delete from.
file REQThe file to delete.

Deletes the specified file.

Example
<cfset application.fc.lib.cdn.ioDeleteFile(location="images",file="/#arguments.stObject[arguments.stMetadata.name]#") />

ioDirectoryExists

ArgumentDescription
location REQThe location to check.
dir REQThe directory to check.

Checks that a specified directory exists. All functions which write to a CDN use this function to check whether the target directory needs to be created first.

Example
<cfset application.fc.lib.cdn.ioDeleteFile(location="images",file="/#arguments.stObject[arguments.stMetadata.name]#") />

ioDirectoryExists

ArgumentDescription
location REQThe location to check.
dir REQThe directory to check.

Checks that a specified directory exists. All CDN functions which create a file already perform internal checks to find out if a directory needs to be created first. As a result, there is no example in core of a call to this function.

Example
<cfif application.fc.lib.cdn.ioDirectoryExists(location="images",file="/#stMetadata.ftDestination#")>
    <!--- something here --->
</cfif>

ioCreateDirectory

ArgumentDescription
location REQThe location to check.
dir REQThe directory to create.

Creates the specified directory, including parent directories. All CDN functions which create a file already perform internal checks and create directories as necessary. As a result, there is no example in core of a call to this function.

Example
<cfset application.fc.lib.cdn.ioCreateDirectory(location="images",file="/#stMetadata.ftDestination#") />

ioGetDirectoryListing

ArgumentDescription
location REQThe location to query.
dir REQThe sub-directory to filter by.

Returns a query containing the files under the specfied directory. The resulting query has a single field "file", which is the full path as would be passed into the other CDN functions. The results are recursive and do not include directories, except by implication.

It is worth noting that the "images" location is unusual in that it usually corresponds to the project WWW directory, and so a naive query to the "images" location will return everything in the webroot. In practice, listings of "images" should be filtered by /images.

Example
<cfset qSourceFiles = application.fc.lib.cdn.ioGetDirectoryListing(location=form.source_location,dir=form.source_filter) />
<cfset qTargetFiles = application.fc.lib.cdn.ioGetDirectoryListing(location=form.target_location,dir=form.target_filter) />            

<cfset stFound = structnew() />
<cfloop query="qSourceFiles">
    <cfif not structkeyexists(stFound,qSourceFiles.file)>
        <cfset stFound[qSourceFiles.file] = 1 />
    <cfelse>
        <cfset stFound[qSourceFiles.file] = stFound[qSourceFiles.file] + 1 />
    </cfif>
</cfloop>
<cfloop query="qTargetFiles">
    <cfif not structkeyexists(stFound,qTargetFiles.file)>
        <cfset stFound[qTargetFiles.file] = 2 />
    <cfelse>
        <cfset stFound[qTargetFiles.file] = stFound[qTargetFiles.file] + 2 />
    </cfif>
</cfloop>

<cfloop collection="#stFound#" item="thisfile">
    <cfset queryaddrow(qFiles) />
    <cfset querysetcell(qFiles,"file",thisfile) />
    <cfset querysetcell(qFiles,"inSource",bitand(stFound[thisfile],1) eq 1) />
    <cfset querysetcell(qFiles,"inTarget",bitand(stFound[thisfile],2) eq 2) />
</cfloop>
            
<cfquery dbtype="query" name="qFiles">select * from qFiles order by file</cfquery>