Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFinNC 2009 (Raleigh, North Carolina) with: Matthew Senn and Michael Senn

Using Plupload For Drag & Drop File Uploads In ColdFusion

By Ben Nadel on

Over at InVision, we use Plupload to enable users to drag files right from their desktop onto the website in order to upload the files to our service. While Plupload has proven to be fairly awesome, getting it up and running smoothly can be a bit of a headache. As such, I thought it would be good to put together a demo project that encapsulates the Plupload instance in a View-Controller and uses the internal events in order to update the Document Object Model (DOM).

Project: View this project on GitHub.


 
 
 

 
  
 
 
 

The markup for the demo is fairly straight forward and consists of a few important parts:

  • Container. This is the DOM element into which Plupload will inject any additional elements required to make the upload work. This may include an Input box that uses the "multiple" attribute; or, it may be a Flash Object that allows multi-file uploads in older browsers (and IE).
  • Dropzone. This is the DOM element on which Plupload will listen for "drop" events. If you want to enable users to drag files from their Operating System (OS) onto their browser, the users have to release the files over this dropzone DOM element.
  • Browse Button. This is the DOM element on which Plupload will listen from "click" events in order to trigger the "Open File" dialogue.

When instantiating the Plupload Uploader, you have to provide it with IDs that correspond to the above elements.

In the demo page below, I am using the same DOM element to define both the Dropzone and Browse Button. This simply means that, in addition to drag & drop, the user can click on the dropzone in order to open the File dialogue:

Index.cfm

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Using Plupload For Drag &amp; Drop File Uploads In ColdFusion
  • </title>
  •  
  • <!-- Style our demo. -->
  • <link rel="stylesheet" type="text/css" href="./assets/css/demo.css"></link>
  •  
  • <!-- Load RequireJS and our bootstrap file. -->
  • <script
  • type="text/javascript"
  • src="./assets/require/require.js"
  • data-main="./assets/main.js">
  • </script>
  • </head>
  • <body>
  •  
  • <h1>
  • Using Plupload For Drag &amp; Drop File Uploads In ColdFusion
  • </h1>
  •  
  •  
  • <!-- BEGIN: Uploader. -->
  • <div id="pluploadContainer" class="uploader">
  •  
  • <!-- BEGIN: Dropzone + Browse Button. -->
  • <a id="pluploadDropzone" class="dropzone html5Dropzone">
  •  
  • <!-- If the HTML5 runtime engine is enabled, show this. -->
  • <span class="instructions html5Instructions">
  • Drag &amp; Drop Your Files Here!
  • </span>
  •  
  • <!-- If the Flash runtime engine is enabled, show this. -->
  • <span class="instructions flashInstructions">
  • Click Here To Select Files!
  • </span>
  •  
  • </a>
  • <!-- END: Dropzone + Browse Button. -->
  •  
  •  
  • <!-- BEGIN: File Queue. -->
  • <div class="queue emptyQueue">
  •  
  • <div class="noFiles">
  • Please select some files to upload!
  • </div>
  •  
  • <ul class="files">
  • <!-- To be populated dynamically. -->
  • </ul>
  •  
  • </div>
  • <!-- END: File Queue. -->
  •  
  •  
  • <!-- BEGIN: Templates For Uploader. -->
  • <script type="application/template" class="templates">
  •  
  • <li class="file">
  • <span class="name">FILE-NAME</span>
  • <span class="progress">
  • (
  • <span class="percentComplete">0%</span> of
  • <span class="totalSize">0</span>
  • )
  • </span>
  • </li>
  •  
  • </script>
  • <!-- END: Templates For Uploader. -->
  •  
  • </div>
  • <!-- END: Uploader. -->
  •  
  •  
  • </body>
  • </html>

Within the dropzone, I am defining instructions for browsers that support drag & drop as well as for browsers that will fallback to Flash. By default, the "HTML5" instructions will show (based on the CSS). Once the Plupload Uploader has initialized, you can check its feature-set to see if the activated "runtime" supports dragdrop. If it doesn't I am changing the dropzone to show the "FLASH" instructions.

If you look at the Script tag on the page, you can see that I am using RequireJS to load the demo. The Plupload library works quite well with RequireJS, using a simple shim to define the library's "export" variable. The RequireJS configuration and the application bootstrap can be seen below.

Main.js - Our Application Bootstrap

  • // Configure our RequireJS paths.
  • require.config({
  •  
  • // Since Plupload doesn't support AMD loading, we can use the shim
  • // configuration to define a just-in-time module that exports the
  • // Plupload library as the named-module, "plupload".
  • shim: {
  • plupload: {
  • exports: "plupload"
  • }
  • },
  •  
  • // Set up the paths to our various modules. Be sure to include the
  • // "full" plupload file - otherwise, you'll get a -500 Init Error.
  • paths: {
  • domReady: "require/domReady",
  • jquery: "jquery/jquery-1.8.0.min",
  • plupload: "plupload/js/plupload.full",
  • views: "views"
  • },
  •  
  • // To help prevent JS caching while we're developing.
  • urlArgs: ("v=" + (new Date()).getTime())
  •  
  • });
  •  
  •  
  • // Run our boostrap file once the DOM-Ready event has fired. Notice
  • // that I am running domReady as a plugin (ie. ends with "!").
  • require(
  • [
  • "jquery",
  • "views/uploader",
  • "domReady!"
  • ],
  • function( $, Uploader ){
  •  
  •  
  • // Create an instance of our upload view giving it the root
  • // module node and the server-side end-point for the uploads.
  • // Since we want to use the Flash runtime, we have to give
  • // it the URL to the Flash engine SWF file to fallback to if
  • // the user's browser doesn't support the HTML5 file API.
  • var uploader = new Uploader(
  • $( "div.uploader" ),
  • "upload_files.cfm",
  • "assets/plupload/js/plupload.flash.swf"
  • );
  •  
  •  
  • }
  • );

In this demo, the Uploader() constructor takes three arguments:

  • A jQuery reference to the root DOM element (the container).
  • The URL endpoint for the ColdFusion file-upload.
  • The URL for the fallback Flash SWF file.

All in all, this demo took me about 3 mornings to put together. It's not hugely complicated; but, wiring up all the events and making sure the individual runtimes work is a bit of a headache. Hopefully, this project will make it easier for people going forward.

NOTE: This project is a bit buggy on my Windows Virtual Machine. I am not sure if this is a byproduct of the VM itself; or, if this project simply has problems running on Windows.




Reader Comments

This is absolutely fantastic, just what I have been waiting for! Just wondering how easy it would be to prevent uploaded filenames from dissapearing from the upload queue after being uploaded and have a small 'x' or icon appear next to it so that it can be removed (without a page refresh) to enable another file to be uploaded in it's place.

A couple of thoughts, How can you limit the number of file uploads to say 6? and how could you stipulate allowable upload file extentensions, i.e. jpg, gif and png?

Would including a thumbnail of the uploaded image be possible?

I think these would be useful features!
In any case, this is a brilliant long awaited solution for integrating Plupload into ColdFusion.

Reply to this Comment

@Paresh,

Glad you like this!

You can certainly stop the filenames from disappearing. That is simply something my view-controller is doing when the upload has completed.

As far as filename filtering, this is definitely something that you can do in the Plupload configuration. In the init() configuration, you can pass in a "filters" property which allows you to specify the types of extensions that can be selected in the file dialogue.

In the example upload (on Plupload):

http://plupload.com/example_custom.php

... you can see that they are applying two filters, for Images and Archives.

As far as limiting the numbers of files, I assume you can do this. It's possible to splice() files out of the file queue; but, I have not personally tried it yet. I'll see if I can give that a go.

Reply to this Comment

It's nifty stuff like this that makes me wish I worked for your startup or Epicenter. I stare all day at ERP and CRM systems, and realize that I'd have to do half the typing if it were in ColdFusion.

Reply to this Comment

Thanks Ben, Not really sure where to begin, but I will give your suggestions a go and post back if I come up with anything.

In the meantime I will wait patiently for any solutions you come up with.

Thanks once again!

Reply to this Comment

Just need to confirm I'm doing the right thing here. I tried adding the 'filters' property to filter file types by jpg,gif and png (in the uploader.js within the views folder):

// Create an initialize the Plupload instance.
this._uploader = new Plupload.Uploader({

// Try to load the HTML5 engine and then, if that's not supported, the Flash fallback engine.
runtimes: "html5,flash",


// Filter files by their extensions
filters : [
{title : "Image files", extensions : "jpg,gif,png"}
],

// The upload URL.
url: this._uploadUrl,

// The ID of the drop-zone element.
drop_element: "pluploadDropzone",

// To enable click-to-select-files, you can provide a browse button. We can use the same one as the drop zone.
browse_button: "pluploadDropzone",

// For the Flash engine, we have to define the ID of the node into which Pluploader will inject the <OBJECT> tag for the flash movie.
container: "pluploadContainer",

// The URL for the SWF file for the Flash upload engine for browsers that don't support HTML5.
flash_swf_url: this._swfUrl

});

But it doesn't work. Am I in the right area of your code Ben?

Reply to this Comment

@Brian,

I appreciate that. We get to solve some cool problems :)

@Paresh,

Yeah, that looks like it's the right area of the code - the initialization config. Is it allowing you to select other kinds of file types?

Reply to this Comment

Hi Ben

Thanks for getting back to me. Appreciated!
Yes, I am able to select other types of file types such as *.txt, *.doc ect.

I have managed to limit the number of files uploaded to 6 (in my case) by placing a counter within the upload page. Once 6 files (images in my case) have been uploaded, it simply ignores additional files.

Just need to try and resolve the file types now! I will keep trying. If you ca think of a way to overcome this, then any assistance would be welcomed!

Thanks once again!

Reply to this Comment

Using your example/demo how would one pass variables into the upload handler? ie upload_files.cfm?eventID=#whatever# . I see where you can hardcode it in main.js, but if I wanted to do it on the fly?

Reply to this Comment

@Jay,

I apologize, I figured out the solution. I am hardcoding the upload handler in main.js (upload_files.cfm) and then appending the variables to the end of this._uploadUrl in uploader.js by attaching ID's to elements on the display page and then pulling them out via jqueries .attr('id') function. Then using these values as the appended variables.

GREAT POST! I love your work!

Reply to this Comment

@Jay,

Yeah, that sounds about right. If you can't hard-code them in the application bootstrap, you'll have to fine a way pull them out of the DOM (such as with an HTML attribute). Good stuff!

Reply to this Comment

Hello Ben, I hope you are doing well after Sandy. We all have seen all the damage and disaster it created.

Just a question, is this script supported in CF8?

Thank you Ben.

Reply to this Comment

I tested the uploader with multiple browsers, it worked Great with Chrome and Firefox, however it did not work with
IE. It currently uploads one file and stops.

I checked a few things and compared it to the example Pupload application and found:

In the developer tools network console the flash initiates a Post.

(example)
/uploader.cfm POST 201 text/html
0.51 KB 0.64 s Flash

However when I use the Drag and Drop ColdFusion Demo it does not record a network connection.

_handlePluploadFileUploaded(), Never fires.

Added a binder for StateChange status and it fires one time and never closes.

Any suggestions or thoughts?
John

Reply to this Comment

Hey Ben, thanks for sharing this. I have a question, have you ever worked with CFWheels? Do you think it's possible to work this into the Wheels framework?

Thanks.

Reply to this Comment

@Dani,

I am not 100% sure. I think it should. There's not much to this code that is actually ColdFusion-specific. Most of it is just JavaScript - as long as you can save the file that is uploaded, it should be fine.

@John,

I do get some wonkiness in IE from time to time. Either the upload fails to start. Or, it gets stuck at 100%. What's odd, however is that when it gets stuck at 100%, the file DID upload - it's just the uploader never stopped for some reason. I have not been able to figure out why this happens sporadically :(

@Amir,

I have not used CFWheels personally; but, we did just integrate this kind of code into a FW/1 application. As long as you get route a POST into a controller method, you should be good to go.

Reply to this Comment

Hi Ben,

Late to the party but thanks for this. I am having an issue with files over 30 mb. I have installed your demo but when I drop and drag a file over 20mb, I get a 404 error on upload_files.cfm. Anything under uploads fine.

Thanks,

Jeff

Reply to this Comment

@Jeff,

Hmm, that's odd that the 404 would be related to file size. Perhaps it could be a limit to the POST size in your ColdFusion administrator? But, if that were the case, I think you'd get a 500 error, not a 404.

Did you try looking in the ColdFusion admin error logs? See if there's something funky going on?

Reply to this Comment

Thanks Ben,

I increased the limit to the post size just in case but that made no difference. I see nothing related to this 404 in the CF Logs either.

Jeff

Reply to this Comment

@Jeff,

Yarrr, that's annoying. Maybe trying adding CFFlush to the top of the "upload" page. If there's an error, sometimes it gets swallowed. But, if you put a CFFlush at the top of the page, sometimes it will force the error to be shown. Then, look in Firebug / Devtools to see what the Plupload response is.

Reply to this Comment

Ben,

I did not see you after Pete Freitag's Lockdown session at cfObjective but he said that IIS sets file size limits at 30MB by default which just happened to be the threshold for file size when I got the 404 errors. I will look at that angle as soon as I revisit this project but I suspect that is the root of the problem.

Thanks,

Jeff

Reply to this Comment

No luck. At least I have uncovered the cause, URLScan 3.1.

Here is what I see in the IIS log when a file is over 30mb.

2013-05-21 23:29:05 10.105.45.128 GET /plupload/assets/jquery/jquery-1.8.0.min.js...

2013-05-21 23:29:13 10.105.45.128 POST /plupload/upload_files.cfm...

2013-05-21 23:29:17 10.105.45.128 GET /Rejected-By-UrlScan ~/plupload/upload_files.cfm 80 ...

I then update the urlscan.ini RequestLimits to

MaxAllowedContentLength=2000000000
MaxUrl=560
MaxQueryString=2048

And IIS request filtering is set to allow up 2gb, as well.

After increasing to the values above, I do an IIS restart but it still stops at 30mb.

When I remove UrlScan from the server, I no longer get the 30MB limit.

Reply to this Comment

Ben,

Thanks so much for this tutorial, just what I was looking for. Having a bit of trouble in IE, but works great in Chrome.

I would like to insert a CFQuery so I can log the file uploaded to a database, how would I capture the file name and at what point would you recommend I place the query?

Any input would be appreciated.

Thanks

Reply to this Comment

If I'm looking at things correctly, looks like I can handle it right from upload_files.cfm.

I will play with it a bit. Any success on getting it stable in IE?

Thx

Reply to this Comment

I've got this working great in all browsers except IE, and I see there are others here who've had this issue. Has anyone had any luck with this?

From what I can see, it seems that IE picks up the _handlePluploadUploadProgress calls once or twice only, and then seems to just...stop. The file is still uploaded though, so there's nothing wrong with the upload part.

It's happening in all IE versions I can get my hands on, from 8 to 11.

Reply to this Comment

Hola Ben estoy probando la aplicación y funciona muy bien, solo me queda una gran duda por mi falta de experiencia y es ¿como puedo pasarle variables URL o CLIENT de índex.cfm a upload_files, ya que no me permite agregar en este ultimo la etiqueta <cfapplication> ya que me marca error?
básicamente estoy tratando que en una sola plantilla se suban archivos a un álbum y usuario especifico, mismos que están declarados en variables URL o CLIENT sin embargo NO FUNCIONA colapsa el script, gracias de antemano por tu respuesta

Reply to this Comment

Hi Ben I'm testing the application and it works great, I just have one big question for my lack of experience and how can I pass URL variables or CLIENT to upload_files index.cfm, it does not allow me to add in this last label <cfapplication> as I mark error?
I'm basically trying to template files in a single album and a specific user, same variables are declared in URL or climb CLIENT DOES NOT collapses however, thanks in advance for your answer script

Reply to this Comment

@Phil,

I added the following and the uploads in IE started working....but the drag and drop still doesn't.

<meta http-equiv="X-UA-Compatible" content="IE=Edge" />

Reply to this Comment

Hello Ben, I'm using this plugin for a client to allow them upload multiple files.
Their application is big and they have multiple forms that may need this feature.
All these forms are displayed in a tabbed environment. This is, in the main page all the forms are <cfincluded>, but they will show only when the right tab is on.
So I added the first implementation and everything worked smooth.
When I added the second implementation, everything stopped working.

Basically when I click the drop hot area in the first form I have implemented, I see the file selection dialog. However it won't open when I do the second implementation.

Can these IDs ( <div id="pluploadContainer" class="uploader">) be part of the problem? If so, should I change also the uploader.js?

I have tried many things and I'm not able to make it work.

Thank you for your help Ben!

Reply to this Comment

@Dani,

The IDs for each Plupload instance most likely need to be unique, especially if you're going to have several instances in the DOM (Document Object Model) at the same time. Regardless of Plupload, your IDs need to be unique on the given state of a page, otherwise the underlying query selector will, at best, get confused, or at worst, not work at all.

Also, if you're going to hide/show difference instances in different tabs, it depends a bit on whether or not the relevant DOM elements are being hidden (ex display:none), or actually removed from the document.

If you're going to remove DOM elements, you probably need to call .destroy() on the Plupload instance when you hide the tab. This way, Plupload can remove the "shim" it puts in place for the click-to-select feature.

But, generally speaking, hopefully it's just an ID problem - make them unique for each instance (that's on the page at the same time).

Reply to this Comment

Hummm...

I copied the GitHub folder on my server and changed the index.htm to index.cfm.

This is what I get (from the application.cfc):

Invalid CFML construct found on line 4 at column 9.
ColdFusion was looking at the following text:
output

The CFML compiler was processing:

A script statement beginning with component on line 3, column 1.
A cfscript tag beginning on line 1, column 2.

The error occurred in C:\wwwroot\test\Application.cfc: line 4
2 :
3 : component
4 : output = "false"
5 : hint = "I define the application settings and event handlers."
6 : {

Any hints ??!

I run CF8

Thanks

Gilles

Reply to this Comment

I think the problem is CF8...which will not take the script notation for the application.

Try wrapping it like this:

<cfcomponent output="false" accessors="true" displayname="Name" name="test">
<cfscript>

what the application.cfc file has between the <cfscript> tag goes here.

</cfscrip>
</cfcomponent>

Reply to this Comment

@dani

No use, doesn't work... Is it possible to have a "translation" of the application.cfc in <cfcomponent> style ?!?

Thanks

Gilles

Reply to this Comment

@dani

I added these lines in your code, just after the <scfscript> and it's working fine:

this.name = hash( getCurrentTemplatePath() );
this.applicationTimeout = createTimeSpan( 0, 0, 10, 0 );
this.sessionManagement = false;

Thanks so much, I'll add the code to insert the image name in the database as well.

Gilles

Reply to this Comment

@dani

In the upload_files.cfm, after the <cffile>, I try to get the file name to insert it in the database.

Unfortunatelly, the #cffile.serverfile# does not seem to work... Any way to go around so I can get the name ?!?

Thanks

Reply to this Comment

After the file is save via cffile (upload_files.cfm), why can't I access it and insert the name in my database with #cffile.serverFile# ??

The code:
<cffile
result="upload"
action="upload"
filefield="file"
destination="#application.uploadsDirectory#"
nameconflict="makeunique"
/>

<cfquery datasource="test">
INSERT INTO images (id, file)
VALUES (#id#, '#cffile.serverFile#')
</cfquery>

Weird...

Gilles

Reply to this Comment

Post A Comment

You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.