- Chunked (split) and normal uploads
- Tested with DropZone.js and simple-uploader.js
- Easily extendable for other vendors
- Supports Adobe 2016, 2018, 2021, and Lucee 5.3.8+
- Install this module into your ColdBox app:
box install UpChunk
- Configure UpChunk by placing a
moduleSettings.UpChunk
structure inconfig/Coldbox.cfc
:
moduleSettings = {
UpChunk : {
/**
* Temporary directory used for storing file chunks during upload.
*/
tempDir : "./tmp/",
/**
* Set the final resting place of uploaded files.
*/
uploadDir : "resources/assets/uploads/",
/**
* Is the `chunkIndex` parameter zero-based?
*/
isIndexZeroBased : true,
/**
* what field names should we look for in the rc memento?
*/
"fields" : {
// points to the location of the uploaded binary
"file" : "fileUpload",
// filename, helpful for creating a user-friendly final filename
"filename" : "filename",
// An id unique to each chunked file upload session for tracking and organized groups of chunks.
"uniqueId" : "dzuuid",
// what chunk index is this current request?
"chunkIndex" : "dzchunkindex",
// total number of upload chunks, helps determine when the file is fully uploaded
"totalChunks": "dztotalchunkcount"
}
}
};
By default, UpChunk ships with DropZone-compatible fields
config. See #Configuration for more details.
- In your ColdBox handler, inject
UpChunk@upchunk
. - Run the
handleUpload()
method, making sure to pass the currentrc
object:
function upload( event, rc, prc ){
var UpChunk = wirebox.getInstance( "UpChunk@upchunk" );
var finalFile = UpChunk.handleUpload( arguments.rc );
writeOutput( "Uploaded file to #finalFile#" );
}
UpChunk primarily works in three steps:
- Parsing the upload. UpChunk reads the provided
rc
memento to determine what sort of upload this is (chunked or non-chunked, i.e. an entire file) and the various parameters specific to the upload, such as which field has the binary, what filename to use, the current chunk number, etc. - Uploading the file or file chunk. If this is an entire file, the upload is complete and UpChunk responds with a result struct.
- For chunked uploads, the final chunk uploaded triggers a compilation process. UpChunk will save the final chunk, and iterate through the entire set of chunks for the current upload, appending each to the final file. Once the file is complete, UpChunk responds with a result struct.
Configuration forsimple-uploader.js.
Note: simple-uploader.js utilizes base 1 numbering for
chunkNumber
andtotalChunks
. For this reason, we need to setisIndexZeroBased: false
.
Note: simple-uploader.js supports a custom
file
parameter name in their frontend options. If you have changed this, please ensure that you change thefields.file
setting to match.
moduleSettings["UpChunk"] = {
/**
* Is the `chunkIndex` parameter zero-based?
*/
isIndexZeroBased : false,
/**
* what field names should we look for in the rc memento?
*/
"fields" : {
// points to the location of the uploaded binary
"file" : "file",
// filename, helpful for creating a user-friendly final filename
"filename" : "filename",
// An id unique to each chunked file upload session for tracking and organized groups of chunks.
"uniqueId" : "identifier",
// what chunk index is this current request?
"chunkIndex" : "chunkNumber",
// total number of upload chunks, helps determine when the file is fully uploaded
"totalChunks": "totalChunks"
}
};
Here's the configuration for DropZone.js support.
Note: DropZone supports a custom
file
parameter name in their frontend options. If you have changed this, please ensure that you change thefields.file
setting to match.
moduleSettings["UpChunk"] = {
/**
* what field names should we look for in the rc memento?
*/
"fields" : {
// points to the location of the uploaded binary
"file" : "fileUpload",
// filename, helpful for creating a user-friendly final filename
"filename" : "filename",
// An id unique to each chunked file upload session for tracking and organized groups of chunks.
"uniqueId" : "dzuuid",
// what chunk index is this current request?
"chunkIndex" : "dzchunkindex",
// total number of upload chunks, helps determine when the file is fully uploaded
"totalChunks": "dztotalchunkcount"
}
};
UpChunk supports the concept of "vendors", i.e. components which perform additional work to add support for a specific javascript uploader vendor, such as simple-uploader.js
via Uploader@upchunk
.
Feel free to write your own vendor component. A typical vendor will extend UpChunk.models.UpChunk
and overwrite the parseUpload()
method:
/**
* FunkyUploader
* Handle abnormal Funky uploads
*/
component extends="UpChunk.models.UpChunk" {
/**
* FunkyUploads does a funny way of chunking,
* so we need to check for some custom form fields to detect the chunk stuff.
*/
function parseUpload( required struct memento ){
// do funky stuff
}
}
Once you've written your vendor, you simply inject it and use it like the standard UpChunk
object:
var upload = getInstance( "MyCustomUploadVendor" )
.handleUpload();
In this example ☝, the handleUpload()
method in UpChunk will call your custom parseUpload()
method, and will then process the upload as normal.
For more complex scenarios, you may find it necessary to extend UpChunk.
You can do this by overwriting any or all of the UpChunk methods outlined in the IChunk.cfc
interface:
/**
* Defines an interface to handle a particular chunk upload vendor.
*
*/
interface {
/**
* Inspect the provided form scope and return info about the current upload (if it is an upload.)
*
* @memento the form scope containing upload parameters. You can pass this from a handler via `UpChunk.handleUpload( arguments.rc )`
*/
public struct function parseUpload( required struct memento );
/**
* Process a non-chunked file upload
* Runs vendor `parseUpload()` event to retrieve upload parameters
* Calls {@see handleChunkedUpload} or {@see handleNormalUpload}, depending on upload.isChunked
*/
public struct function handleUpload( required struct memento );
/**
* Process a file upload chunked
* @upload {Struct} parameters for upload, parsed from event and defined in vendor parseUpload() method
*/
public string function handleChunkedUpload( required struct upload );
/**
* Handle final merging of all upload chunks into a single file
* Executed only on upload of last file chunk.
*
* @upload {Struct} parameters for upload, parsed from event and defined in vendor parseUpload() method
* @returns String - returns path to completed file
*/
public string function mergeChunks( required struct upload );
}
Note: Looking for a new feature, or hoping to add an upload vendor? Consider opening a new issue or adding a Pull Request?
- All contributions welcome!
- Feel free to write a test, fix a README typo, or add a new vendor
To get started hacking on UpChunk:
- Clone the module -
git clone [email protected]:michaelborn/UpChunk.git
- Install dependencies -
box install
- Run tests -
cd tests && box testbox run
- Write code
- Run tests
- Push up a pull request
- Pull original filename from form field parts
- Add docs for more upload vendors
For all have sinned, and come short of the glory of God (Romans 3:23)
But God commendeth his love toward us, in that, while we were yet sinners, Christ died for us. (Romans 5:8)
That if thou shalt confess with thy mouth the Lord Jesus, and shalt believe in thine heart that God hath raised him from the dead, thou shalt be saved. (Romans 10:9)
Copyright 2021 (and on) - Michael Born