File Uploads
Handle file uploads with automatic temporary storage, validation, and processing. CBWIRE automatically converts uploaded files into FileUpload objects with methods for accessing content, metadata, and preview URLs.
Basic Usage
Create a photo upload component with save functionality:
// wires/PhotoUpload.bx
class extends="cbwire.models.Component" {
data = {
"photo": "",
"isUploading": false
};
function save() {
if (data.photo != "") {
// Move file to permanent location
var storedPath = data.photo.store(expandPath("./uploads"));
// Clean up temporary file
data.photo.destroy();
// Reset form
data.photo = "";
}
}
}// wires/PhotoUpload.cfc
component extends="cbwire.models.Component" {
data = {
"photo" = "",
"isUploading" = false
};
function save() {
if (data.photo != "") {
// Move file to permanent location
var storedPath = data.photo.store(expandPath("./uploads"));
// Clean up temporary file
data.photo.destroy();
// Reset form
data.photo = "";
}
}
}<!-- wires/photoUpload.bxm -->
<bx:output>
<div>
<h1>Upload Photo</h1>
<form wire:submit.prevent="save">
<div>
<label for="photo">Select Photo:</label>
<input type="file"
id="photo"
wire:model="photo"
accept="image/*">
</div>
<!-- Loading indicator -->
<div wire:loading wire:target="photo">
Uploading photo...
</div>
<!-- Preview uploaded photo -->
<bx:if photo != "" AND photo.isImage()>
<div class="preview">
<h3>Preview:</h3>
<img src="#photo.getPreviewURL()#"
alt="Uploaded photo"
style="max-width: 200px;">
<p>Size: #photo.getSize()# bytes</p>
</div>
</bx:if>
<button type="submit"
wire:loading.attr="disabled">
Save Photo
</button>
</form>
</div>
</bx:output><!-- wires/photoUpload.cfm -->
<cfoutput>
<div>
<h1>Upload Photo</h1>
<form wire:submit.prevent="save">
<div>
<label for="photo">Select Photo:</label>
<input type="file"
id="photo"
wire:model="photo"
accept="image/*">
</div>
<!-- Loading indicator -->
<div wire:loading wire:target="photo">
Uploading photo...
</div>
<!-- Preview uploaded photo -->
<cfif photo != "" AND photo.isImage()>
<div class="preview">
<h3>Preview:</h3>
<img src="#photo.getPreviewURL()#"
alt="Uploaded photo"
style="max-width: 200px;">
<p>Size: #photo.getSize()# bytes</p>
</div>
</cfif>
<button type="submit"
wire:loading.attr="disabled">
Save Photo
</button>
</form>
</div>
</cfoutput>How It Works
CBWIRE handles file uploads through a multi-step process:
Request signed URL - CBWIRE gets a temporary upload URL from the server
Upload file - JavaScript uploads the file to secure temporary storage
Create FileUpload object - The data property becomes a FileUpload instance
Process file - Use FileUpload methods to validate, manipulate, or store the file
Store permanently - Move files to permanent storage using the
store()method
By default, CBWIRE stores uploaded files in the system's temporary directory (via getTempDirectory()) for enhanced security. This prevents unauthorized access to uploaded files before they're explicitly moved to permanent storage. You can configure a custom temporary storage path using the uploadsStoragePath setting. This is useful for distributed server environments where temporary files need to be shared across multiple servers. See the Configuration documentation for details.
FileUpload Methods
When a file is uploaded, CBWIRE creates a FileUpload object with the following methods:
File Content
get()
Returns the binary content of the uploaded file
getBase64()
Returns base64 encoded string of the file
getBase64Src()
Returns base64 data URL for use in <img> tags
File Information
getSize()
Returns file size in bytes
getMimeType()
Returns the MIME type of the file
getMeta()
Returns all metadata about the uploaded file
isImage()
Returns true if the file is an image
File Locations
getTemporaryStoragePath()
Returns the temporary file storage path
getMetaPath()
Returns path to file metadata
getPreviewURL()
Returns URL for previewing images
File Storage
store( path )
Moves file from temporary to permanent storage. Path can be a directory or full file path. Creates destination directories automatically. Returns absolute path to stored file.
Cleanup
destroy()
Deletes temporary file and metadata (call after saving)
Always call destroy() on FileUpload objects after saving to permanent storage to clean up temporary files.
Storing Files Permanently
The store() method moves uploaded files from temporary storage to a permanent location. This is the recommended approach for persisting uploaded files.
Basic Usage
Pass a directory path to store the file with its original filename:
function save() {
if (data.photo != "") {
// Store in directory with original filename
var storedPath = data.photo.store(expandPath("./uploads"));
writeLog("File stored at: #storedPath#");
data.photo.destroy();
}
}function save() {
if (data.photo != "") {
// Store in directory with original filename
var storedPath = data.photo.store(expandPath("./uploads"));
writeLog("File stored at: #storedPath#");
data.photo.destroy();
}
}Store with Custom Filename
Pass a full file path to store with a custom filename:
function save() {
if (data.document != "") {
// Store with custom filename
var customPath = expandPath("./uploads/#createUUID()#.pdf");
var storedPath = data.document.store(customPath);
// Save path to database
saveDocumentPath(storedPath);
data.document.destroy();
}
}function save() {
if (data.document != "") {
// Store with custom filename
var customPath = expandPath("./uploads/#createUUID()#.pdf");
var storedPath = data.document.store(customPath);
// Save path to database
saveDocumentPath(storedPath);
data.document.destroy();
}
}Automatic Directory Creation
The store() method automatically creates destination directories if they don't exist:
function save() {
if (data.avatar != "") {
// Directory will be created if it doesn't exist
var userUploadDir = expandPath("./uploads/users/#data.userId#");
var storedPath = data.avatar.store(userUploadDir);
data.avatar.destroy();
}
}function save() {
if (data.avatar != "") {
// Directory will be created if it doesn't exist
var userUploadDir = expandPath("./uploads/users/#data.userId#");
var storedPath = data.avatar.store(userUploadDir);
data.avatar.destroy();
}
}After calling store(), the FileUpload object's internal path is updated to the new location. You can continue to use FileUpload methods like get() or getSize() on the stored file. However, you should call destroy() after storing to clean up the metadata file in temporary storage.
Loading States
Display upload progress using wire:loading directives:
<input type="file" wire:model="document">
<div wire:loading wire:target="document">
Uploading document...
</div>
<button type="submit" wire:loading.attr="disabled">
Save Document
</button>Handling Upload Errors
The onUploadError() lifecycle hook is automatically called when a file upload fails with any HTTP response outside the 2xx range. This allows you to handle upload failures gracefully and provide feedback to users.
Method Signature
function onUploadError( property, errors, multiple )property
string
The name of the data property associated with the file input
errors
any
The error response from the server. Will be null unless the HTTP status is 422, in which case it contains the response body
multiple
boolean
Indicates whether multiple files were being uploaded (true) or a single file (false)
Usage Example
// wires/PhotoUpload.bx
class extends="cbwire.models.Component" {
data = {
"photo": "",
"uploadFailed": false,
"errorMessage": ""
};
function onUploadError( property, errors, multiple ) {
// Set error state
data.uploadFailed = true;
// Create user-friendly error message
data.errorMessage = "Failed to upload " & ( multiple ? "files" : "file" ) & " for " & property;
// Log the error for debugging
if ( !isNull( errors ) ) {
writeLog( type="error", text="Upload error for #property#: #serializeJSON(errors)#" );
}
}
}// wires/PhotoUpload.cfc
component extends="cbwire.models.Component" {
data = {
"photo" = "",
"uploadFailed" = false,
"errorMessage" = ""
};
function onUploadError( property, errors, multiple ) {
// Set error state
data.uploadFailed = true;
// Create user-friendly error message
data.errorMessage = "Failed to upload " & ( multiple ? "files" : "file" ) & " for " & property;
// Log the error for debugging
if ( !isNull( errors ) ) {
writeLog( type="error", text="Upload error for #property#: #serializeJSON(errors)#" );
}
}
}<!-- wires/photoUpload.bxm -->
<bx:output>
<div>
<input type="file" wire:model="photo">
<bx:if uploadFailed>
<div class="alert alert-danger">
#errorMessage#
</div>
</bx:if>
</div>
</bx:output><!-- wires/photoUpload.cfm -->
<cfoutput>
<div>
<input type="file" wire:model="photo">
<cfif uploadFailed>
<div class="alert alert-danger">
#errorMessage#
</div>
</cfif>
</div>
</cfoutput>When It's Called
The onUploadError() hook is triggered when:
The upload server returns any HTTP status code outside the 2xx range (200-299)
Network errors occur during upload
The server is unreachable
Any other upload failure scenario
Error Information
The errors parameter provides different information based on the HTTP status:
422 (Unprocessable Entity): Contains the full response body (typically validation errors)
Other error codes: Will be null
You can check the response status in your server logs or implement custom error handling based on your application's needs.
Last updated
Was this helpful?