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 cf.Objective() 2012 (Minneapolis, MN) with:

Using Multiple Dropzones And File Inputs With A Single Plupload Instance

By Ben Nadel on

Yesterday, when I was looking at the latest release of Plupload (v2.0 beta), I found a wonderful playland of classes that had been factored out of the original Plupload library as a means to make Plupload more flexible. And, when I saw that they had added a way to programmatically add files to a Plupload instance, I got excited that I may finally be able to associate multiple dropzones with a single Plupload instance - something that I don't believe had been possible in the past.


 
 
 

 
 
 
 
 

Run this demo in my Plupload Multiple Dropzones project on GitHub.

With the recent updates, the Plupload authors have factored out the classes - FileDrop and FileInput - as a means to independently add drag-drop functionality and file selection functionality to a given DOM element. As the same time, they have also added the method, addFile(), to the Plupload instance. This addFile() method can accept instances of the mOxie.File object, which is exactly what the FileDrop and FileInput classes emit. Pretty freakin' sweet!

To try and wire several of these dropzones into a single Plupload instance, I have put together this demo (which builds on yesterday's image preview demo):

  • <!doctype html>
  • <html>
  • <head>
  • <meta charset="utf-8" />
  •  
  • <title>
  • Using Multiple Dropzones With A Single Plupload Instance
  • </title>
  •  
  • <link rel="stylesheet" type="text/css" href="./assets/css/styles.css"></link>
  • </head>
  • <body>
  •  
  • <!-- BEGIN: Main Demo Area. -->
  • <div id="uploader" class="uploader">
  •  
  • <h1>
  • Using Multiple Dropzones With A Single Plupload Instance
  • </h1>
  •  
  • <!--
  • NOTE: I didn't really want a "select files" item; however,
  • the "drop" aspect of the main Plupload / Uploader does not
  • seem to work properly without it. I guess because not all
  • of the runtimes support drag/drop... but they ALL support
  • a "select files" feature.
  • -->
  • <p id="buttonContainer">
  • <a id="button" href="#">Select Files</a>
  • </p>
  •  
  • <ul class="uploads">
  • <!-- Will be populated dynamically with LI/IMG tags. -->
  • </ul>
  •  
  • </div>
  • <!-- END: Main Demo Area. -->
  •  
  • <!--
  • These are the auxilary dropzones which will tie into the
  • primary dropzone / uploader above.
  • -->
  • <ul class="dropzones">
  • <li class="dropzone nw">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone n">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone ne">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone e">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone se">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone s">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone sw">
  • <a href="#">Drop / Select</a>
  • </li>
  • <li class="dropzone w">
  • <a href="#">Drop / Select</a>
  • </li>
  • </ul>
  •  
  •  
  • <!-- Load and initialize scripts. -->
  • <script type="text/javascript" src="./assets/jquery/jquery-2.0.3.min.js"></script>
  • <script type="text/javascript" src="./assets/plupload/js/plupload.full.min.js"></script>
  • <script type="text/javascript">
  •  
  • (function( $, plupload ) {
  •  
  •  
  • // Find and cache the DOM elements we'll be using.
  • var dom = {
  • uploader: $( "#uploader" ),
  • uploads: $( "ul.uploads" ),
  • dropzones: $( "ul.dropzones li.dropzone" )
  • };
  •  
  •  
  • // Instantiate the Plupload uploader.
  • var uploader = new plupload.Uploader({
  •  
  • // Try to load the HTML5 engine and then, if that's
  • // not supported, the Flash fallback engine.
  • runtimes: "html5,flash",
  •  
  • // The upload URL - for our demo, we'll just use a
  • // fake end-point (we're not actually uploading).
  • url: "./post.json",
  •  
  • // The ID of the drop-zone element.
  • drop_element: "uploader",
  •  
  • // To enable click-to-select-files, you can provide
  • // a browse button. We can use the same one as the
  • // drop zone.
  • browse_button: "button",
  •  
  • // 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: "buttonContainer",
  •  
  • // The URL for the SWF file for the Flash upload
  • // engine for browsers that don't support HTML5.
  • flash_swf_url: "./assets/plupload/js/Moxie.swf",
  •  
  • // Needed for the Flash environment to work.
  • urlstream_upload: true
  •  
  • });
  •  
  •  
  • // Set up the event handlers for the uploader.
  • // --
  • // NOTE: I have excluded a good number of events that are
  • // not relevant to the current demo.
  • uploader.bind( "PostInit", handlePluploadInit );
  • uploader.bind( "FilesAdded", handlePluploadFilesAdded );
  •  
  • // Initialize the uploader (it is only after the
  • // initialization is complete that we will know which
  • // runtime load: html5 vs. Flash).
  • uploader.init();
  •  
  •  
  • // Now that we've initialized the pluploader, let's wire
  • // in the rest of the peripheral dropzones. Each dropzone
  • // will act as both a "drop" area as well as a "file
  • // select" input.
  • dom.dropzones.each(
  • function() {
  •  
  • // -- Configure DROPZONE. -- //
  •  
  • // Wiki: https://github.com/moxiecode/moxie/wiki/FileDrop
  • var dropzone = new mOxie.FileDrop({
  • drop_zone: this
  • });
  •  
  • // When the event is fired, the context (ie, this)
  • // is the actual dropzone. As such, you can access
  • // the files using any of the following:
  • // --
  • // * this.files
  • // * dropzone.files
  • // * event.target.files
  • dropzone.ondrop = function( event ) {
  •  
  • // The addFile() method can accept an individual
  • // file or an array of file sources.
  • uploader.addFile( dropzone.files );
  •  
  • };
  •  
  • dropzone.init();
  •  
  • // -- Configure FILEINPUT -- //
  •  
  • // Wiki: https://github.com/moxiecode/moxie/wiki/FileInput
  • var input = new mOxie.FileInput({
  • browse_button: $( "a", this )[ 0 ],
  • container: this,
  • multiple: true
  • });
  •  
  • // When the event is fired, the context (ie, this)
  • // is the actual input. As such, you can access
  • // the files using any of the following:
  • // --
  • // * this.files
  • // * input.files
  • // * event.target.files
  • input.onchange = function( event ) {
  •  
  • // The addFile() method can accept an individual
  • // file or an array of file sources.
  • uploader.addFile( input.files );
  •  
  • };
  •  
  • input.init()
  •  
  • }
  • );
  •  
  •  
  • // ------------------------------------------ //
  • // ------------------------------------------ //
  •  
  •  
  • // I handle the init event. At this point, we will know
  • // which runtime has loaded, and whether or not drag-
  • // drop functionality is supported.
  • // --
  • // NOTE: For this build of Plupload, I had to switch from
  • // using the "Init" event to the "PostInit" in order for
  • // the "dragdrop" feature to be correct defined.
  • function handlePluploadInit( uploader, params ) {
  •  
  • console.log( "Initialization complete." );
  •  
  • console.log( "Drag-drop supported:", !! uploader.features.dragdrop );
  •  
  • }
  •  
  •  
  • // I handle the files-added event. This is different
  • // that the queue-changed event. At this point, we
  • // have an opportunity to reject files from the queue.
  • function handlePluploadFilesAdded( uploader, files ) {
  •  
  • // Show the client-side preview using the loaded File.
  • for ( var i = 0 ; i < files.length ; i++ ) {
  •  
  • console.log( "File added:", files[ i ].name );
  •  
  • showImagePreview( files[ i ] );
  •  
  • }
  •  
  • }
  •  
  •  
  • // I take the given File object (as presented by
  • // Plupoload), and show the client-side-only preview of
  • // the selected image object.
  • function showImagePreview( file ) {
  •  
  • var item = $( "<li></li>" ).prependTo( dom.uploads );
  • var image = $( new Image() ).appendTo( item );
  •  
  • // Create an instance of the mOxie Image object. This
  • // utility object provides several means of reading in
  • // and loading image data from various sources.
  • // --
  • // Wiki: https://github.com/moxiecode/moxie/wiki/Image
  • var preloader = new mOxie.Image();
  •  
  • // Define the onload BEFORE you execute the load()
  • // command as load() does not execute async.
  • preloader.onload = function() {
  •  
  • // This will scale the image (in memory) before it
  • // tries to render it. This just reduces the amount
  • // of Base64 data that needs to be rendered.
  • preloader.downsize( 100, 100 );
  •  
  • // Now that the image is preloaded, grab the Base64
  • // encoded data URL. This will show the image
  • // without making an Network request using the
  • // client-side file binary.
  • image.prop( "src", preloader.getAsDataURL() );
  •  
  • // NOTE: These previews "work" in the FLASH runtime.
  • // But, they look seriously junky-to-the-monkey.
  • // Looks like they are only using like 256 colors.
  •  
  • };
  •  
  • // Calling the .getSource() on the file will return an
  • // instance of mOxie.File, which is a unified file
  • // wrapper that can be used across the various runtimes.
  • // --
  • // Wiki: https://github.com/moxiecode/plupload/wiki/File
  • preloader.load( file.getSource() );
  •  
  • }
  •  
  •  
  • })( jQuery, plupload );
  •  
  • </script>
  •  
  • </body>
  • </html>
  • ͛

This demo has eight (8) peripheral dropzones. And, using the FileDrop and FileInput constructors, I am able to pipe the various file-selection events into the addFile() method on the main Plupload instance. This has some seriously awesome implications; like being able to have a persistent uploader that dynamically connects to transient dropzones and file inputs. Yo, the Plupload guys are killing it with this new release!




Reader Comments

@All,

As a note, I should say that you have to be a little bit careful with the FileDrop as it won't necessarily be supported by all browsers. While I haven't tested this specifically, what I *assume* you would have to do would be to bind to the "onready" event and assume that it will only fire *if* the browser supported drag-drop events.

When I first approached the demo, I actually was using the "ready" event to call .show() on the peripheral dropzones; but, I dropped that (no pun intended) to simplify the demo.

Reply to this Comment

@Mohannad,

Now that you mention that, I know that I had to do some IE-10-specific tweaking to my Plupload code in our production app. I wish I could remember what it was! IE10 was the first IE to allow for Drag-Drop and it started breaking (even though Firefox and Chrome and Safari all worked properly).

Oooh, I remember what it was! In my production code (which does NOT use Plupload v2 Beta), I have the "container" and the "dropzone" be the same element. With the non-IE browsers, this works fine because the Input that gets dynamically added has a "z-index: -1", which puts it behind everything else.

But, with IE, the omit the z-index so the Input ended up covering the dropzone. As such, the "click to select files" would work in IE10 but, the drag-drop feature would not work (even though it was supported by the runtime).

I know this is off-topic, but I ended up fixing this with some hacky JavaScript. Basically, if the Input received a "drag" event, I dynamically hid it (to expose the dropzone). And, if the exposed dropzone received the "mouseover" event, I would show the Input (to hide the dropzone).

I think I did that ONLY in the IE browser and let everything else work as it always had.

Anyway, off topic, but I just wanted to get that off my chest :)

Reply to this Comment

Hi Ben,

This is a great post. One question I had on extending this - do you know if there's an easy way to identify which dropzone/bucket the files were dropped into and subsequently attach a multiparam value to those files? So for example, on upload, these files will have a dropzone_id of "NE", etc...

Thanks!

Reply to this Comment

Thank you. This is helpful stuff. I am wondering if you may know how I can relate the drop event back to the dropzone element. There are some uuid's that are generated. One is added as an ID to the dropzone element. ondrop(event) has event.target.uid and ruid... however but I can't find anything that matches between the two. Ultimately I am simply trying to put the filename displayed on the dropzone used... then I'm planning to manage the file buffer so that if you were to drag a different file to the same dropzone it would remove the previously dropped file from the queue and add the new. Thanks!

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.