The No.1 i-Technology Magazine in the World !
   
 
Straxus
cat /dev/kbd | grep --random-lines > straxus.javadevelopersjournal.com
Ryan Slobojan (Straxus) is a
Software Consultant with
Codesta LLC
(http://www.codesta.com)
««
January 2009
»»
SM
T
WTFS
     123
45678910
11121314151617
18192021222324
25262728293031
Search
 

RSS2.0
ATOM
New entries
Comments
Mailing List




Creating a Mozilla/Firefox Drag and Drop file upload Script (Part 1)

There has been a lot of focus lately on web applications which have enhanced rich-client functionality, such as the oft-published AJAX approach adopted by Google Maps and GMail. One of the features which is present for desktop applications but which is difficult to accomplish with a web page is drag and drop of something that didn’t start off on the webpage (for example, dragging an image around a webpage is fairly easy to do, however dragging a file or shortcut from the desktop onto the webpage is much more difficult). In addition, although such functionality exists and is fairly easy to access with an ActiveX control for Internet Explorer, it is difficult to replicate inside of a Mozilla/Firefox browser due to the inherent complexities of cross-platform support as well as the stronger security model.

However, difficult does not mean impossible, and this article will describe how to create a control which allows a user to drag a file onto a webpage and upload it (automatically if desired) to a given server. I will mention now that this first part of the discussion will create a file which functions correctly on the local machine – Part 2 will deal with the difficulties of packaging up this code in a Jar, signing that Jar, and deploying it on a remote server.

Overview of what will be done

Before we dive in, I will give you a quick run-down of what we will accomplish with this code example. The steps which need to be followed are:

  1. Register for the Mozilla/Firefox drag and drop event, and attach an event handler

  2. Request permission to access the Mozilla XPConnect bridge

  3. Retrieve the low-level platform drag and drop event from the operating system (wrapped by a Mozilla XPCOM object)

  4. Determine if the data being dropped is in a format which we can handle

  5. Assuming valid file data, get the filename(s) which are being dropped onto the app

  6. Request permission to read a file from the local file system

  7. Save the filename(s) in the upload form’s file input control

  8. Marking the event as handled

Registering for the Drag and Drop event

The first thing that we need to do is tell the Mozilla/Firefox window that we want to register for the drag-and-drop event. Now, the way it would normally work for an event is that we would do the following:
window.addEventListener("dragdrop", dragDropHandler, true);
This would register the method dragDropHandler with the dragdrop event for the window. However, just doing this will not work correctly. We need to first tell the browser window explicitly that we want drag and drop events, and then register for the event. Code for this looks like:
window.captureEvents(Event.DRAGDROP);
window.addEventListener("dragdrop", dragDropHandler, true);
Finally, we’ll want to defer doing this until the page has loaded, to ensure that all the HTML and scripts are present before we try to use this. So, the final code for this will look like:
function loadMethod()
{
// We need to register the window to capture Drag & Drop events
// before we can receive them with our event handler.
window.captureEvents(Event.DRAGDROP);

// Register the event handler, and set the 'capture' flag to true
// so we get this event before it bubbles up through the browser.
window.addEventListener("dragdrop", dragDropHandler, true);
}

<body onload=”loadMethod();” …>

Requesting permission to access the XPConnect bridge

In Mozilla, the main browser functionality is wrapped in a library called XPCOM. This library is what handles most of the lower-level interactions with the host operating system, and is designed with cross-platform considerations in mind. If you wish to access XPCOM functionality from within a web page of some kind, you must use the XPConnect bridge which handles interfacing between XPCOM and Javascript. For a much more in-depth explanation of how all of these components work together, please see the Mozilla XPConnect Page.

Since XPConnect provides a lot of system access, permission to access it is only given after a security check. In order to request this security check, we must do the following:
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
This will ask the browser for the ability to communicate with the XPConnect bridge. If the page is not already a trusted page, this will cause a dialog to appear for the user asking them if they wish to grant permission for the page to "install and run software" (which I suppose is the primary use of the XPConnect bridge).

A couple of things about the Mozilla permissions system need to be pointed out right now, as they will affect how this drag and drop example is built:

  • Only a script on the local machine’s file system or a signed script on a webpage can request that a privilege be enabled. If you attempt to call this method from an unsigned script, it will throw an exception. This does not matter for Part 1, but is the reason for Part 2 of this discussion

  • An enablePrivilege request has the same scope as a variable – if you are inside a method and request enablePrivilege, that privilege will go away when the method’s execution ends. Also, requesting the permission in a <SCRIPT> block on page startup will *not* grant that permission for the whole page – it will go away with the </SCRIPT> tag that ends the block

As a result of the second point, we must include the enablePrivilege call inside of our drag and drop handler, i.e.:
function dragDropHandler (evt) {

// Request the XP Connect privilege so we can access the XPCOM API.
// This will cause a dialog to be displayed to the user asking them
// if they want to grant this privilege to this script.
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

}

Retrieve the Drag and Drop Event from Mozilla

Now we’re going to get into the interesting stuff – how to load classes from XPCOM which handle the drag and drop operation. To start with, we need to load in the Mozilla Drag Service. This is done with:
var dragService =
Components.classes["@mozilla.org/widget/dragservice;1"]
.getService(Components.interfaces.nsIDragService);
What this line does is load in the Mozilla drag service class from the table of classes, and then retrieve the nsIDragService class (API Reference for nsIDragService available here) from that loaded class and return it. This class handles the creation and destruction of drag and drop sessions, and we will use it to retrieve the current drag-and-drop session with:
var dragSession = dragService.getCurrentSession();
We now have the in-progress drag and drop session, which is an nsIDragSession (API Reference for nsIDragSession available here). In order to draw the data out of this session, we need to create a new nsITransferable (API Reference for nsITransferable available here) and choose the types of drag-and-drop data we want to receive. This is done with:
var transferObject =
Components.classes["@mozilla.org/widget/transferable;1"]
.createInstance();

// Bind the object explicitly to the nsITransferable interface. We need to do
// this to ensure that methods and properties are present and work as expected
// later on.
transferObject =
transferObject.QueryInterface(Components.interfaces.nsITransferable);
Take note of the second line of code above – the nsITransferable needs to be explicitly created/casted using the QueryInterface call to ensure that all the properties and methods of nsITransferable are present on the Javascript object.

Determining the Format of the Data being Dropped

So we’ve created an nsITransferable object, but we don’t yet know what types of data are present in the drag and drop session. In order to check if certain MIME types are supported by the session, we would call:
var isSupported = dragSession.isDataFlavorSupported("application/x-moz-file");
This will return a boolean (true or false) indicating whether the given MIME type is supported. For the sake of this example, I have foregone checking of the MIME type of the session data – this will result in an error if the thing dragged onto the Mozilla window is not a file.

Based on the assumption that a file will be present, I have chosen to only request the application/x-moz-file data flavor from the drag-and-drop session – this is the MIME type of a file, and it is all I want to be dropped. However, you can choose any MIME type you want - a list of supported MIME types is available here. The code to set the requested MIME types is:
transferObject.addDataFlavor("application/x-moz-file");
This will cause only the application/x-moz-file data to be retrieved from the Drag Session which is in progress.

Retrieving Filenames from the Drag session

Finally, we get to what we’ve wanted to do all along – retrieve the names of the files being dropped onto the browser window. First, we’ll want to figure out how many files are being dropped, which is done with:
var numItems = dragSession.numDropItems;
This will allow us to loop through each dragged item and handle it individually. Next, we want to enter the loop and grab the actual items from the drag session. This is done with:
for (var i = 0; i < numItems; i++)
{
// Get the data for the given drag item from the drag
// session into our prepared Transfer object.
dragSession.getData(transferObject, i);

}
This will retrieve the given item from the Drag Session into the nsITransferable object which we created.

At this point we have the dragged file in a Transfer object, but we still need to retrieve the specific data-type from the Transfer object. In order to do this, we need to call the transferObject.getTransferData method (Method API docs available here). However, you will note that this method has two parameters listed as OUT parameters – what that means is that return values for this method will be returned in those objects. In order to properly handle an XPCOM OUT parameter, we need to create new Object()s and pass those in as the OUT parameters, and then check the Object’s value property. An example of how to do all of this (and return the data object at the same time) is:
// We need to pass in Javascript 'Object's to any XPConnect method which
// requires OUT parameters. The out value will then be saved as a new
// property called Object.value.
var dataObj = new Object();
var dropSizeObj = new Object();

// Get the Mozilla File data type from the ITransferable object.
transferObject.getTransferData("application/x-moz-file", dataObj, dropSizeObj);

// Display the return value of this OUT parameter.
alert(“Dropped object size is “ + dropSizeObj.value);
As we can see, we have created two new OUT parameters, called the getTransferData method to retrieve the dragged data, and displayed the value of the second OUT parameter in a dialog box using the Object.value property.

Request File System Read Permissions

Before we can set the value of a File input field, we must request local file read permissions. This is done with:
netscape.security.PrivilegeManager.enablePrivilege('UniversalFileRead');
Please note that if we don’t do this, we will get a security exception when we attempt to set the value of the file input field. Also remember the restrictions on enablePermissions which were described in the Request permission to access the Mozilla XPConnect bridge section – the same restrictions apply here.

Save the Filename to the Form’s File Input Field

At long last, we’ve reached the end. In order to save the filename of the dragged file into the form’s file input field, we need to take the value of the data object which we were returned from the getTransferData call and cast it as an nsIFile (API Reference for nsIFile available here). Then, we take the path from the nsIFile and save it in the file input field’s value property. This is done with:
var droppedFile = dataObj.value.QueryInterface(Components.interfaces.nsIFile);

var fileIn = document.getElementById("fileInputField");

fileIn.value = droppedFile.path;
Note the QueryInterface call to ensure that the nsIFile capabilities were applied. Also take note of the retrieval of the file input field – in this example, we will retrieve the same file input field every time through the loop and overwrite it’s value with the value of the next dragged file. If we wanted to do something more intelligent like add a new file input field for each file, we could use DOM to create a new file input node, e.g.:
var newFileIn = document.createElement(“input”);

newFileIn.setAttribute("type", "file");
newFileIn.setAttribute("name", "fileData" + i);
newFileIn.setAttribute("id", "fileData" + i);

var parentForm = document.getElemendById(“formID”);
parentForm.appendChild(newFileIn);
It would also probably be advisable to set the form to be hidden, so the user isn’t seeing all of these file input fields appearing and such. This can be done by setting the form to have a style of ‘visibility: hidden’, e.g.:
<form
id="formID"
action="/path/to/uploadServlet"
method="post"
enctype="multipart/form-data"
style="visibility: hidden;"
>
Also take note of the encoding type of multipart/form-data – since we’re uploading a file, we will want to encode with this type to ensure the file isn’t mangled in transit (as the default form encoding of application/x-www-form-urlencoded will do).

At this point, the file data is saved in the form. When the form is submitted, it will be uploaded to the server. Depending upon your application, you may wish for this to be an automatic process – in that case, you would simply call formObject.submit() at the end of your Javascript method, and the form would be uploaded.

Marking the Event as handled

Now that we’re done and we’ve handled the event, make sure to let Mozilla know! If you don’t mark the event as handled, then the default handler will also kick in and do unexpected and undesirable things (like open the document inside of the current window). This marking is done with:
evt.stopPropagation();
Where evt was the event that we were passed in to our drag and drop handler.

Full code for this discussion

This code is the full code of the sample which I built. I have included several debugging output statements which were not discussed above, which show the state of the drag and drop operation at different points. It must also be noted that, in order to have a production-level set of usable code, you will want to implement error checking and exception catching (which I have left out as it contributes nothing but clutter to the demonstration of functionality). In particular, every permission check can throw an exception if the permission is not granted, so that should be handled in a more graceful manner.
<HTML>
<HEAD>
<SCRIPT language='Javascript'>

// This code comes from an article which is
// Copyright (C) 2005, Codesta LLC. All Rights Reserved.
// All of the research that I did for this article was
// on company time, and Codesta agreed to let me publish it.
// The information in it is free for you to use, but don't
// go claiming it's yours - it's not.

// I leave this as an explicit method because it comes in handy a lot when debugging issues.
function listAllMozillaXPCOMInterfaces(elemToWriteTo)
{
// Pass this through to the property-listing method.
listAllPropsOnObject(Components.interfaces, elemToWriteTo);
}

// Lists all of the properties on the given object by writing them as text to the given page element.
function listAllPropsOnObject(objToList, elemToWriteTo)
{
elemToWriteTo.appendChild(document.createElement("br"));
elemToWriteTo.appendChild(document.createTextNode("Properties for [" + objToList + "]:"));
elemToWriteTo.appendChild(document.createElement("hr"));

for ( var prop in objToList ) {
with (objToList)
{
elemToWriteTo.appendChild(
document.createTextNode("[" + prop + "] = " + eval(prop))
);
elemToWriteTo.appendChild(document.createElement("br"));
}
}

elemToWriteTo.appendChild(document.createElement("br"));
elemToWriteTo.appendChild(document.createElement("br"));
}

// These security privileges are what are needed to do the interesting things we may want to do.
// Please note that you need to request the privilege within the scope of when you intend to use
// the associated permission - for example, I cannot declare the privileges in the Load method
// because the privilege will expire when the load method ends. You can think of this like a
// standard variable - it's available in the declaring scope (and for any child function calls)
// but goes away when the end curly-brace for that scope is reached.
// Each is summarized below.

// This allows us to read and upload files from the local filesystem. If we don't get this
// privilege, we can't read from the file system manually. When we get to the point that
// we are uploading the dragged file to the server, this permission will be required.
//netscape.security.PrivilegeManager.enablePrivilege('UniversalFileRead');

// This allows us to access the Mozilla XPConnect API. We need to do this so that we can read
// from the native event system. The Javascript event that is given to us when a Drag/Drop
// occurs has no native data in it id the drag/drop started outside the browser window, so
// we need to use XPCOM (Via XPConnect) to get at the native drag/drop event.
//netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

// A final note about these privileges - a privilege can only be requested by a script that is:
//
// a) on the local machine
// b) inside of a signed JAR
//
// So, when we want to deploy this to the server, we will need to JAR it up and sign it.

function dragDropHandler (evt) {

// Select the element to write out all of our data to.
var draggee = document.getElementById("dragTableData");

// Request the XP Connect privilege so we can access the XPCOM API. This will
// cause a dialog to be displayed to the user asking them if they want to grant
// this privilege to this script.
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');

// Write out all of the XPCOM Interfaces that are available via the XPConnect bridge.
listAllMozillaXPCOMInterfaces(draggee);

// Load in the native DragService manager from the browser.
var dragService =
Components.classes["@mozilla.org/widget/dragservice;1"]
.getService(Components.interfaces.nsIDragService);

// Load in the currently-executing Drag/drop session.
var dragSession = dragService.getCurrentSession();

// List off all of the properties that are on the Drag/drop session.
listAllPropsOnObject(dragSession, draggee);

// Create an instance of an nsITransferable object using reflection.
var transferObject =
Components.classes["@mozilla.org/widget/transferable;1"]
.createInstance();

// Bind the object explicitly to the nsITransferable interface. We need to do this to ensure that
// methods and properties are present and work as expected later on.
transferObject = transferObject.QueryInterface(Components.interfaces.nsITransferable);

// This list of flavors was retrieved from http://www.mozilla.org/xpfe/xptoolkit/DataFlavors.html.
// This is a simple debug message which indicates which flavors are supported by the data currently
// on the drag/drop clipboard.
alert(
"text/x-moz-url? " + dragSession.isDataFlavorSupported("text/x-moz-url") + "\n" +
"text/unicode? " + dragSession.isDataFlavorSupported("text/unicode") + "\n" +
"text/xif? " + dragSession.isDataFlavorSupported("text/xif") + "\n" +
"text/html? " + dragSession.isDataFlavorSupported("text/html") + "\n" +
"text/plain? " + dragSession.isDataFlavorSupported("text/plain") + "\n" +
"image/png? " + dragSession.isDataFlavorSupported("image/png") + "\n" +
"image/jpg? " + dragSession.isDataFlavorSupported("image/jpg") + "\n" +
"image/gif? " + dragSession.isDataFlavorSupported("image/gif") + "\n" +
"application/x-moz-url? " + dragSession.isDataFlavorSupported("application/x-moz-url") + "\n" +
"application/x-moz-file? " + dragSession.isDataFlavorSupported("application/x-moz-file") + "\n" +
"AOLMAIL? " + dragSession.isDataFlavorSupported("AOLMAIL")
);

// I've chosen to add only the x-moz-file MIME type. Any type can be added, and the data for that format
// will be retrieved from the Drag/drop service.
transferObject.addDataFlavor("application/x-moz-file");

// Get the number of items currently being dropped in this drag/drop operation.
var numItems = dragSession.numDropItems;
var fileIn = document.getElementById("fileInputField");

// Request the 'file read' privilege. We need to do this in order to be able to set the value of
// a FileInput box. If we don't do this, we'll get a security exception when we try to set that
// value.
netscape.security.PrivilegeManager.enablePrivilege('UniversalFileRead');

for (var i = 0; i < numItems; i++)
{
// Get the data for the given drag item from the drag session into our prepared
// Transfer object.
dragSession.getData(transferObject, i);

// Read off all of the properties on this transfer object.
listAllPropsOnObject(transferObject, draggee);

// Start creating a String to hold all the text that will be in a debug alert.
var supportedTypes = "transferobject supported transfer types array is:\n";

// Get the set of all flavors supported by this Transfer object.
var typeArray = transferObject.flavorsTransferableCanExport();
var curItem = null;

// Iterate through the array of supported MIME types, and write them all out to the
// temporary output string.
for (var j = 0; j < typeArray.Count(); j++)
{
curItem = typeArray.GetElementAt(j);

// Cast this object as a C String via the QueryInterface method.
curItem = curItem.QueryInterface(Components.interfaces.nsISupportsCString);

supportedTypes += (curItem + "\n");
}

// Display our list of supported flavors
alert(supportedTypes);

// We need to pass in Javascript 'Object's to any XPConnect method which
// requires OUT parameters. The out value will then be saved as a new
// property called Object.value.
var dataObj = new Object();
var dropSizeObj = new Object();

// Get the Mozilla File data type from the ITransferable object.
transferObject.getTransferData("application/x-moz-file", dataObj, dropSizeObj);

// Cast the returned data object as an nsIFile so that we can retrieve it's filename.
var droppedFile = dataObj.value.QueryInterface(Components.interfaces.nsIFile);

// Display all of the returned parameters with an Alert dialog.
alert("dataObj filename is " + droppedFile.path + ", num is " + dropSizeObj.value);

// Set the File Input box's value to be the path to the file that was just dropped.
fileIn.value = droppedFile.path;
}

// Since the event is handled, prevent it from going to a higher-level event handler.
evt.stopPropagation();
}

function loadMethod()
{
// We need to register the window to capture Drag & Drop events before we can receive them with
// our event handler.
window.captureEvents(Event.DRAGDROP);

// Register the event handler, and set the 'capture' flag to true so we get this event
// before it bubbles up through the browser.
window.addEventListener("dragdrop", dragDropHandler, true);
}

</SCRIPT>
</HEAD>
<BODY onload="loadMethod();">
<FORM NAME="formName" id="fileForm"
ACTION="yourFileUploadHandler"
METHOD="post"
ENCTYPE="multipart/form-data"
>
<INPUT TYPE="file" NAME="fileName" id="fileInputField">
<INPUT TYPE="submit">
</FORM>

<table><tr><td id="dragTableData">
This is a cell which will be filled with debugging info.
</td></tr></table>
</BODY>
</HTML>


As a final note, this article is Copyright (C) 2005, Codesta LLC. All Rights Reserved. All of the research that I did for this article was on company time, and Codesta agreed to let me publish it. The information in it is free for you to use, but don't go claiming it's yours - it's not.
ironstorm made this comment,
There is a typeo in the full source listing...

.getServiceComponents.interfaces.nsIDragService);

should be: .getService(Components.interfaces.nsIDragService);

comment added :: 29th June 2005, 09:35 GMT-05
Straxus made this comment,
Hmm, so there is.

Updated.

Thank you very much!

comment added :: 29th June 2005, 09:46 GMT-05 :: http://straxus.javadevelopersjournal.com
Trevan made this comment,
Running this code from my linux destop, I get a "Component Returned failure code". It is referencing the getTransferData function. Any idea why I get this error? I am using Firefox 1.04.
comment added :: 18th July 2005, 15:20 GMT-05
Straxus made this comment,
I think that the reason you are getting this error is that the data contained in the drag/drop operation is not of type application/x-moz-file. On the line that's throwing the error:

transferObject.getTransferData("application/x- moz-file", dataObj, dropSizeObj);

You will notice that it's only grabbing out data of type application/x-moz-file. If the data in the clipboard is of a different type, then you will get the error you reported.

During the course of this demo, there is an alert() box which pops up and checks the data in the drag/drop session against all of the possible types (e.g. alert("text/x-moz-url? " + dragSes sion.isDataFlavorSupported("text/x-moz-url"));) -- take a look at which types of data are in the drag/drop session. It might be that whatever window manager you are using is passing in a text string or something instead of a file.

comment added :: 23rd July 2005, 09:24 GMT-05 :: http://straxus.javadevelopersjournal.com
Priyu made this comment,
Thanks alot,it will be great help for me
comment added :: 26th November 2005, 06:55 GMT-05
jj made this comment,
is there a demo anywhere of this function?
comment added :: 5th December 2005, 05:25 GMT-05
Straxus made this comment,
There is no online example as such, however if you want to test out how this works you can just copy the entire contents of the large chunk of sample code at the end of the article, save it as an HTML file, and open that file in Mozilla or Firefox. Since it is running on your local machine, you will be able to try out the drag-and-drop functionality of the sample without worrying about the jar packaging and certificate signing which is required for putting this code on an actual live webpage.
comment added :: 5th December 2005, 22:37 GMT-05 :: http://straxus.javadevelopersjournal.com
FreddytheFish made this comment,
Wow, this looks like a solution to a problem I'm having with some drag and drop functionality that I need to add into a web application.

Thank you! Kudos! Bravo! and all of that.

comment added :: 13th December 2005, 19:11 GMT-05
Napolux made this comment,
I wonder if exist a more "crossbrowser" way to do this.
comment added :: 17th February 2006, 07:05 GMT-05 :: http://www.napolux.com
Straxus made this comment,
To the best of my knowledge, the short answer is no. If you want to do something like this for Internet Explorer, you end up going through COM or ActiveX. With Mozilla/Firefox, it's the XPCOM bridge. Because of the fact that it's a fairly complicated and (from a security perspective) risky thing to do, each browser handles it in a different way which is dependent upon the internal plumbing of the browser. As an example, I wouldn't even know where to start if I was implementing this for Opera or Konqueror.

Until there's some very comprehensive standardization of the web experience from a client perspective (e.g. a standard method of interfacing with the end operating system), this will need to be done on a per-browser basis. However, I think such a standardization is a minimum of 10 years out - a lot of other things need to be nailed down before that, and I'm pessimistic that such a standardization will ever occur.

comment added :: 17th February 2006, 12:00 GMT-05 :: http://straxus.javadevelopersjournal.com
ATLAS made this comment,
Hello, and thanks for sharing your code. This is exactly what I've been looking for, but I'm having some problems implementing this. I did like you said and I copied the large chunk of code and saved it as an html file. When I open the page however, I get a regular looking browse button and the input text. Am I doing something wrong? How do I drag the files from the desktop to the page and have it upload it? Please help, I spent the whole day looking for such a script and this is as close as I've gotten.
comment added :: 20th February 2006, 23:57 GMT-05
Straxus made this comment,
The page you have loaded is correct - it starts off with just a standard file upload dialog. However, try dragging and dropping a file onto that page (anywhere on the page area is fine) - you will then see several dialogs pop up describing the different phases of the drag and drop operation, and at the very end the file upload box will be populated with the name of the file to be uploaded, without you having to select the 'Browse' button.

This demo is meant to be just that - a demo. In order to have a complete, automated upload process you will want to do something like:

  • Create an <input> tag with javascript in a form that is kept in the HTML header - this will allow you to keep the form off the page, but still manipulate it from Javascript via DOM

  • Add the upload handler code which is in this demo, and have it add the filename to the <input> element you created

  • After the file is added, do a form.submit() in Javascript - this will submit the form to the server, thus uploading the file

You also need something server-side to "catch" the file - a form has an upload destination (e.g. /servlets/fileupload) and that is what catches the file and does whatever with it.

There are also other things you can do - what I did with a sample application was have an area on the page that handled file uploads, and I allowed the user to upload pictures. Once a picture was dragged onto the page, I uploaded it to the server, created an iFrame on the local HTML page with a source that would allow them to download the picture they just uploaded as a thumbnail, and loaded that iFrame. The end result was dragging a picture onto a web page, and seeing that picture appear in the HTML of the page.

comment added :: 21st February 2006, 13:44 GMT-05 :: http://straxus.javadevelopersjournal.com
ATLAS made this comment,
I tried dragging a random jpg file on the page just now, and instead of getting the pop-up dialogs, I get the actual picture displayed on the page. I'm thinking that something has got to be disabled or not working properly. Do you have any ideas what might be wrong?
comment added :: 21st February 2006, 14:24 GMT-05
Fish made this comment,
You need to make sure you're not running it off of a server. If you are then you need to get a certificate and have it installed on your browser. If its locally accessed and giving you this error, then you've done something wrong.
comment added :: 25th February 2006, 23:23 GMT-05
woktiny made this comment,
I got this working right away, then combined it with what I found at http://www.captain.at/ajax-file-upload.php and had a quick and dirty ajax D&D uploader. I even managed to eliminate the need for a form completely.
comment added :: 14th March 2006, 16:57 GMT-05
Mike made this comment,
How would someone go about specifying a particular area on a webpage that could accept dropped files? (Instead of the whole page)
comment added :: 9th May 2006, 15:27 GMT-05
dipesh made this comment,
nice
comment added :: 7th June 2006, 11:51 GMT-05
Benjamin Hawkes-Lewis made this comment,
Mike:

Perhaps a simple example would answer you best.

Let's say you want to handle drop events on a particular "img" element on your webpage with an ID of "myimage". In the LoadMethod() function, grab it with the DOM:

myImg = document.getElementById('myimage');

Then, rather than adding the event listener to window, add it to your element:

myImg.addEventListener('dragdrop', dragDropHandler, true);

... and you're done :)

Ryan,

Thanks for this fabulous tip. You say that: "such functionality exists and is fairly easy to access with an ActiveX control for Internet Explorer". Do you know of any examples of this that still work? I've been googling around and I can't find any. Microsoft seem to have made it difficult to captured dropped files with DHTML for security reasons: "Currently, for data security reasons, Internet Explorer prevents any drop target in the browser from accessing data that originates in another security domain or in another desktop application." (http://msdn.microsoft.com/workshop/ author/datatransfer/overview.asp)

comment added :: 24th June 2006, 04:20 GMT-05
Matt made this comment,
Incredible! It's amazing how few people have published such an example of this. I've been searching for hours. Your solution is elegant and can be (locally) implemented with a single html file. Awesome. Thank you.
comment added :: 9th August 2006, 15:51 GMT-05
dyrek made this comment,
Straxus.. this is very impressive and easy to implement. Thank you so much for the legwork.. Woktiny, please publish your tip.
comment added :: 1st November 2006, 16:36 GMT-05
dyrek made this comment,
Please explain what you mean by: If you are then you need to get a certificate and have it installed on your browser...

I would love to get this to work on a webserver.

comment added :: 1st November 2006, 16:37 GMT-05
Dave made this comment,
Hi Straxus,

Thanks for the tutorial. This information is incredibly hard to find on the web because Googling Drag and Drop and Javascript inevitably leads to thousands of pages showing how to drag and drop something from on a page to somewhere else on the same page. I was really happy to find this.

But just like it was hard to find this, it is even harder to find elaborations on this. I would like to have drag and drop functionality from outside of the browser to a page in the browser, but I would like to define my own DataFlavor and MIME type. Basically, I would like to be able to drag an instance of a class which contains a String s, a bool b, and an int i onto the page and if b is true, then I would like to display i copies of s on the page. I can handle things from the Drag Source end of things, but I haven't been able to get the data to interact well with the browser.

I ended up encoding the class as a String which I would drag into the browser and then parse to get the original data out. But then the user can drag the String into all sorts of places where it doesn't belong (like the address bar or notepad etc.) and see information that they should not be allowed to see. If I had a custom MIME type that only part of the page accepted, then I figure the data could be hidden from the user in a more professional manner since everything else would reject the drop.

Your tutorial is the closest thing I can find to what I want to do and you even mention MIME types and DataFlavors. I feel like I am so close to the answer I have been frustrated by for way too long now. Do you (or anyone else who reads this comment) have any idea where this information can be found?

Thanks a lot.

comment added :: 3rd January 2007, 10:59 GMT-05
Tref Gare made this comment,
Many thanks for documenting this process - I've been playing with it and it's very promising. Thought I should share a typo that I didn't notice for some time that was driving me crazy. I was trying to implement dynamic addition of file fields but it was escaping me.. it would always immediately open the dragged in file in the browser window.

Eventually I found it. In the following line

var parentForm = document.getElemendById(“formID”);

there's an errant "d" should be

var parentForm = document.getElementById(“formID”);

comment added :: 6th March 2007, 20:34 GMT-05
rgtaylor made this comment,
Just want to say that anyone looking for cross browser solutions should look at using a Java Applet... Current security recommendations have deprecated much of the methods used here. Get a professionally signed cert to sign your jar with, build you applet to do whatever you need including handle drop actions and copy files from the client and send them to your server... then jar the applet, sign the jar with the professional cert and include the applet as the drop target on your page... most professional applications don't allow dropping just anywhere on the site, they make you drop, as he said above, on a specific location, this double ensures your intent....

The steps are a bit more involved than I have specifically stated here, but not too much and much easier than messing with mozilla security, and it works in FF, Netscape, Opera, IE, and etc.

And you don't have to code anything special to deal with security in your applet... though it is a good idea to build in specific routines to check functions are available prior to using them, and move on gracefully when the particular function is not available...

Once your applet has security cleared it can use http, ftp, etc. to handle anything you like including contact servers other than that from which it came...

Peace

comment added :: 11th April 2007, 22:58 GMT-05
tom made this comment,
Does anyone have a link to a similar tutorial on the web that explains how to achieve the same functionality in IE6/7 using activex controls?
comment added :: 8th June 2007, 03:53 GMT-05
Matze made this comment,
I need a tutorial for IE6/7 with ActiveX too. I hope someone have information about that.

Thanks you very much!

comment added :: 17th July 2007, 08:57 GMT-05
rgth made this comment,
How can I restrict the drop to only one node. For example: I will have a div in the page, only on which the dragged files can be dropped. So how can I specify that drop target ?
comment added :: 3rd October 2007, 03:28 GMT-05
barry made this comment,
This is amazing, I was convinced this is not possible. Three questions tho, 1. I get 2 security popups saying the operation is unsafe (I don't select 'Remember my decision'). Will the user always get 2 popups (per file dropped) if a page uses this technique? (I'm using ff v2.0.0.7 and win xp SP2). 2. Is it possible to impliment Ctrl C, Ctrl V support for files or not? 3. Is this really cross platform? Does it work on all environments where firefox can be run?
comment added :: 8th December 2007, 05:50 GMT-05
Rod made this comment,
To kind of understand the part 2 that he hasn't posted you can go here http://books.mozdev.org/html/mozilla-chp-12-sect-6 .html It explains the process fairly well.
comment added :: 11th March 2008, 13:00 GMT-05
ashutosh made this comment,
hi, i am doing same as per your suggestion, but still i have problem. when i access this page on client side then it is not working. pls suggest me correct answer as well as in Internet explorer also. thanks in advance
comment added :: 28th July 2008, 04:59 GMT-05
zedwood made this comment,
Interesting... but take a look at this drag and drop javascript library. All you have to do is specify the 'drag' container and the clickable 'handle' container and off you go, any javascript or dhtml container is able to drag and drop.
comment added :: 6th October 2008, 14:46 GMT-05 :: http://zedwood.com/article/104/Javascript_-_Drag_a

This blog is created and maintained by the author of the page and in no way associated with SYS-CON Media or JDJ. The author of the blog assumes all liability and responsibility personally for the content of the page. JavaTM, J2EE, J2ME, J2SE, and other Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON and JDJ are independent of Sun Microsystems.
www.blog-n-play.com is a registered trademark (78553120) of SYS-CON Media.