Using Multiple Dropzones And File Inputs With A Single Plupload Instance
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!
Want to use code from this post? Check out the license.
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.
Thank you Ben for this nice post.
Unfortunatelly it is not working on IE 10/11
I did upgraded the PlUpload plugin from the GitHub and it fixed the issues.
@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 :)
Thanks! Just what i needed and something that definitely should be included in plupload docs somewhere!
very Helpful !!
Thanks !! :)
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!
This is exactly what I was looking for, thanks
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!
hi,
how can i preload images into the upload area when the page is loaded or refreshed?
let's say i have two images:
$pic1 = "uploads/photo.jpg";
$pic2 = "uploads/anotherphoto.jpg";
i want them to auto load every time the page is loaded or refreshed.
if max_files_count is set to 5, there will be 3 slots available for more pics, in this example.
thanks
Epic, Ben. Thanks so much!
is there a way to find out witch dorp /select box was used by the drop-files event?i have added id's to the html, and now want to know to witch one was used.
<li class="dropzone nw">
... id = "ds1"href="#">Drop / Select
</li>
<li class="dropzone n">
... id="ds2" href="#">Drop / Select
</li>
best regards
stefan