Drag and Drop File Uploading Made Easy

by Zack Grossbart on November 28, 2007

Everyone has had the experience of uploading images to a web site. You are given a set of 10 browse buttons and have to select each file you want to upload, or worse have to go back to the same page over and over again and select each file. Most applications provide a nice drag and drop interface which allows you to easily select multiple images (as well as other files) and add them to your documents.

This article demonstrates a simple and easy way to let users of your website drag and drop files from their file system and upload them to your server. In addition to being easier for your users to use, this program will zip all the files into one archive for faster uploading. This program is a sample application with full source code, allowing you to customize and integrate this functionality into your own web application.

This application has two parts: a Java applet and a Java servlet. You could achieve similar functionality using Internet Explorer and an ActiveX control, but Java is the only multi-browser cross-platform option available today. This sample will run on Windows, Linux, MacOS, and any other platform where Java is available. Most computers already have a Java JDK installed, but if you don’t have one you can download it from http://www.javasoft.com.

A version of this application with PHP on the server is available on the Panyasan’s Random Musings. This version replaces the Java servlet with a PHP script and include some good enhancements to the Java applet.

The code in this application is free and is released under the Apache 2.0 license. That means you are welcome to use, copy, and change this code as much as you would like. If you find any bugs, have any comments, or make any improvements I would love to hear from you. There are a couple of other programs needed to run this sample, but they are all free.

Setup

Get the
source code

Get the Source Code

You should start by downloading the source code for this sample. It can be found here.

Get Other Programs

This program has been setup to be built using Apache Ant. Ant is a very popular build tool for Java projects. Building this project without Ant is possible, but it can be tricky to set up all the pieces. If you don’t have Ant already you should download and install it..

This program will generate a WAR file which requires a servlet container to run. It should work with any J2EE compliant server. I use Jetty, an open source web server. It is light, fast, and free.

Build and Run

Once you have installed Ant and Jetty you can build and run the sample application by following these steps:

  1. Unzip the samples archive to a directory on your machine.
  2. Open a command prompt and change directories to the location you unzipped the sample in.
  3. Execute the command ant (you may need to provide the full path to your Ant installation).
  4. Copy the WAR file dist/dnddemo.war to the webapps directory of your Jetty server.
  5. Start the Jetty server. (The start command is java -jar start.jar.)
  6. Open a browser and go to http://localhost:8080/dnddemo/imageadd.htm. This URL will be different if you use a different server.

Core Technologies

This program will use the following core technologies:

  • Java Applets
  • Java Servlets
  • Signed JARs
  • Drag and Drop
  • HTTP
  • Java Swing
  • Java IO

This example will demonstrate a Java applet contained in a signed JAR and using drag and drop, a Java servlet, simple HTTP connections, a basic Java Swing based application, and use of the Java Input/Output libraries.

How It Works

This program consists of a servlet and a signed applet. The servlet will accept files being uploaded to the server and the applet will provide those files. The applet must be signed because it will need to access files on the user’s machine.

This program is completely browser-based. It can be accessed with the following URL: http://localhost:8080/dnddemo/imageadd.htm. This page will show a warning the first time it is run.

warning

This warning appears because the applet is not signed by a certificate authority. This applet was signed locally as part of the build process. This will be discussed more in the section about building the application. You can run this applet safely since you have just built it, but you should always read messages like this carefully and never run applications from sources you do not trust.

Once you choose to run this applet you will see the drag and drop applet demo image uploader.

applet

You can drag images (as well as other files) from your desktop or any other location on your computer and drop them on the label at the top of the applet. Once you have dropped all of the files you want, press the upload button to send those files to the server.

When you press the upload button the applet will gather all of the selected files and add them to a zipped archive. It will then transmit that zip archive to the servlet. The servlet will read the archive and save each file to a directory on the server. The servlet will then display the list of images that have been uploaded and allow you to view them in your browser.

Let’s Look at the Code

This application consists of two files DNDApplet.java and ImageSrv.java. The DNDApplet is responsible for gathering the files and uploading them to the server. The ImageSrv is responsible for receiving those files and then displaying them back to the user.

DNDApplet.java

DNDApplet.java extends java.applet.Applet and implements DropTargetListener. Extending the applet class allows our class to run in the bowser and implementing drop target listener interface allows our class to accept drag and drop gestures. This allows the class to operate inside the browser and accept drag and drop events from programs outside of the browser. Let’s take a look at the code to handle drag and drop.

The first step is to register our control as a drop target. This will let Java know that our control is a valid place to drop items that are dragged from somewhere else. That code is part of the init method and it looks like this:

JLabel title = new JLabel("Drag and Drop Files Here");
DropTarget dt = new DropTarget(title, this);

This code snippet will tell Java that the label is a drop target and that this class will handle the drag and drop events generated by that action. This is why DNDApplet implements DropTargetListener.

Registering as a DropTargetListener will generate events when the user drags over your target, enters your target, exits your target, and finally drops on your target. We are only interested in the drop event. The code behind that event is very simple. Let’s take a look:

public void drop(DropTargetDropEvent dtde)
{
    int action = dtde.getDropAction();
    dtde.acceptDrop(action);1
    fromTransferable(dtde.getTransferable());2
    dtde.dropComplete(true);3
}

1. Accept the drop action.
2. Get the data from the drop event.
3. Notify the JVM that the drop event has been completed so it can clear the drag and drop event with the native operating system.

That is all it takes. Now let’s take a look at the basics of getting the data from the drop event. The full code for this method can be found in DNDApplet.java in the sample code.

private void fromTransferable(Transferable t)
{
    DataFlavor flavors[] = t.getTransferDataFlavors();1
    if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)2) {
        List list = (List) t.getTransferData(DataFlavor.javaFileListFlavor);
        m_fileList.addAll(list);3

        StringBuffer sb = new StringBuffer();
        sb.append("<HTML><UL>");
        for (int i = 0; i < m_fileList.size(); i++) {
            File f = (File) m_fileList.get(i);
            sb.append("<LI>" + f + "</LI>\n");
        }
        sb.append("</UL></HTML>");4

        m_statusLabel.setText(sb.toString());

        m_updload.setEnabled(true);5
    }
}

We will start by getting all of the data flavors from the drop event1.. A drag and drop event can originate from any program on your machine. The data associated with the event could be in any of a large number of formats. The source of a drag and drop event may offer multiple flavors of data, allowing the drop target to pick the one that best suits its needs. These data flavors will be defined by the source of the drag and drop even. For example, a drag and drop of a piece of text from OpenOffice might provide one data flavor containing just the selected text and another data flavor containing the full formatting information. This is the same mechanism used to support copy, cut, and paste between different applications.

This application is interested in the javaFileListFlavor2. type. When we see this flavor, we know that the user is dragging one or more files onto our applet. The data in the drag and drop event will be a list of the files. We can then add these files to our list of files to upload to the server3.. Once we have added the files to the list we want to display those files to our users. We can take advantage of the HTML support in JLabels to produce some nice formatting when we add each file to the list4.. Finally we enable the upload button5. so the user can start the upload process.

ImageSrv.java

When the user presses the upload button the ImageSrv will be contacted. ImageSrv is an HttpServlet and it handles uploading and displaying the files from the applet. Files are uploaded to the servlet using an HTTP PUT request. This is a request just like the one that comes from browsers – it will not cause problems with firewalls and proxy servers. Let’s take a look at a simplified version of the doPut method. The full version can be found in ImageSrv.java in the sample code.

public void doPut(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException
{
    ZipInputStream in = new ZipInputStream(request.getInputStream());1

    File dir = new File("webapps/dnddemo/images");
    dir.mkdirs();2

    try {
        while (true) {
            ZipEntry entry = in.getNextEntry();
            if (entry == null) {
                break;3
            }
            File f = new File(dir, entry.getName());
            FileOutputStream out = new FileOutputStream(f);
            try {
                int read;
                byte[] buf = new byte[1024];

                while ((read = in.read(buf)) > 0) {
                    out.write(buf, 0, read);4
                }
            } finally {
                if (out != null) {
                    out.close();
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        in.close();
    }

    response.setContentType(CONTENT_TYPE);
    PrintWriter out = response.getWriter();
    out.println("%lt;html>%lt;head>%lt;title>ImageSrv%lt;/title>%lt;/head>%lt;/html>");
    out.flush();
    out.close();5
    response.setStatus(HttpServletResponse.SC_OK);6
}

1. The applet has provided all the uploaded files as a zip archive. We need to read the files out of the archive and write them to the server’s local file system.
2. We need to make sure the directory exists before we start adding files to it.
3. When we hit the end of the zip file the next entry will be null.
4. Write out each file to the file system, 1024 bytes at a time.
5. Return a response to the applet so it knows that the files are done uploading.
6. Return a response code of OK to indicate that everything was successful.

Once we have uploaded the files to the server we want to display them to the user. The servlet will show a list of all the files as well as showing each file. The servlet uses an HTTP parameter to determine which image to show. For example you can see a list of all the files at this address:

http://localhost:8080/dnddemo/imageview.htm

You can display a specific image using this address, replacing “image1.jpg” with your image’s filename:

http://localhost:8080/dnddemo/imageview.htm?image=image1.jpg

Let’s take a look at a simplified version of the doGetImage method. The doGetImage method is responsible for delivering images and other files to the browser. The full version can be found in ImageSrv.java in the sample code.

public void doGetImage(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
{
    String image = request.getParameter("image");
    if (image.endsWith(".jpg")) {
        response.setContentType("image/jpeg");
    } else if (image.endsWith(".gif")) {
        response.setContentType("image/gif");
    } else if (image.endsWith(".png")) {
        response.setContentType("image/png");
    } else {
        response.setContentType("application/binary");1
    }

    readFile(image, response.getOutputStream());2
}

When a web server returns a file to a browser it also includes a mime type. The mime type or content type lets the browser know how to handle a file from the server. Most servlets use the content type of text/html to indicate that their response is in HTML format. When we return images and other files we need to set the appropriate content type. This method will supply the correct content type for JPG, GIF, and PNG files. For all other files we return a content type of application/binary1.. This content type tells the browser that this a generic binary file. The browser will most likely prompt the user to save the file rather than trying to display it. Once we have set the correct content type we can send the actual bytes of the file down to the browser2..

There is a useful reference list of mime types available at https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types.

How it Gets Built

Most Java applications are built in two steps: a compiler creates Java class files from the Java source code, and then a build program like ant packages the class files into JARs, WARs, and other archives. This program requires the additional step of signing the applet JAR. The warning message displayed when running the applet is because this JAR was signed with a certificate authority meant only for testing. If you sign this applet with a certificate from an established certificate authority like VeriSign you will not see this warning message.

Building and Signing the JAR

A set of Java classes can be put together in a JAR (Java ARchive) and delivered as a single application. Normally a JAR file consists of the files needed to run the application and a manifest file that describes the JAR file. Most applets are packaged in JAR files to allow faster downloading and better code organization.

Applets run inside a special applet security manager. This security manager restricts the actions code within the applet can take and protects your computer from malicious code. Applets are generally not allowed to read files from your local file system. Our applet requires extra permission to do so. In order to gain that extra permission our applet JAR must be signed.

Signing a JAR will ensure that it was really written by the person or group who says they wrote it and that the code has not been changed since it was signed. The most common way to do this is to use a certificate authority. The developer of the application can obtain a certificate of authenticity from the certificate authority. This certificate can be presented along with the application to ensure the validity of the application.

Creating a valid certificate takes more time and money than is reasonable for a sample application. This application will use a certificate authority meant just for testing. This is why you will see the warning mentioned earlier when you load the applet.

The signed portion of this application will look like this:

Signature-Version: 1.0
SHA1-Digest-Manifest-Main-Attributes: 4mBNxPp0vnlZ33Hos81NvtUDJQk=
Created-By: 1.6.0_01 (Sun Microsystems Inc.)
SHA1-Digest-Manifest: 1O/wwDSPkGFPLrSI+0gStTLxld0=

Name: dndapplet/applet/DNDApplet.class
SHA1-Digest: Sq7jYIN9MvZ87IONaokNDB9MK7U=

There will also be an additional section added to the JAR’s manifest file which will look like this:

Name: dndapplet/applet/DNDApplet.class
SHA1-Digest: awxXry4+saVToZprtEbntMnCbTI=

As part of the build process for this application a key store will be created. This key store will hold the private part key used to sign this application. This key store will then be used to sign the applet JAR file. There are more comments and specific steps about this process in the build.xml file included with this sample.

For more information about signed JARs you can see the documentation for the jarsigner tool included with the Java Development Environment. It can be found at http://java.sun.com/javase/6/docs/technotes/tools/windows/jarsigner.html.

A Note About Security

This sample will allow users to take files from their computers and post them to a directory on your server. This is a potentially dangerous thing to allow. You have no way of knowing if the file being posted is a harmless image or a malicious script file. You should always make sure that the permissions for user files you store on your server specify that the files cannot be run on your server. You might also consider storing the files in a database instead.

Conclusion

Providing a good user experience is vital to the success of your application. Extending the drag and drop metaphor of the desktop to the browser is a great way to make your application easy to use. You can add a real certificate to this applet and use it in production very quickly.

This application has been made simple for demonstration purposes and can easily be expanded. You can take this sample and adapt it to your web application. You could also add progress bars, better error handling, or whatever features you can imagine. There is also much more information, comments, and sample code in the source archive for this sample.