Drag & Drop Upload Applet - How to simulate HTTP MULTIPART POST

Hi there!

First off: It's been quite a while since i was messing with java the last time. I usually use rails in my projects. But there are some things you just can't do in rails... *gg*

Having said that, here is my mission:

I am trying to put together a java-applet, later on to be used in my current rails project to upload multiple files to a server-sided database. the applet will function as a drop-target, that invokes a http multipart post handled by some rails upload action.

I got this far: I set up an applet that accepts filelists (selections of 1-n files) to be dropped on it from e.g. a windows filebrowser. the applet then displays the correct path values of the dropped assets.

What I need to do: Invoke a multipart form post and transmit the files to the server.

What wold be a huge plus: Tell the user that his/her data is being transferred by displaying some sort of statistics and / or progress-bar while uploading - but first things first - here is my current code:

import java.awt.*;

import java.awt.dnd.*;

import java.awt.datatransfer.*;

import java.awt.event.*;

import java.io.*;

import java.util.*;

import javax.swing.*;

publicclass DropUploaderextends JAppletimplements DropTargetListener{

public DropTarget dt;

private JTextArea ta;

privatestaticfinallong serialVersionUID = 1;

public String uh ="http://server.com/uploadhandler.cgi";

publicvoid init(){

try{

duInit();

}catch(Exception e){

e.printStackTrace();

}

}

publicvoid duInit()throws Exception{

setSize(600,300);

ta =new JTextArea();

ta.setText("Drop files here.");

ta.setBackground(Color.white);

getContentPane().add(ta, BorderLayout.CENTER);

// Set up our text area to receive drops...

// This class will handle the drop events

dt =new DropTarget(ta,this);

setVisible(true);

}

publicvoid dragEnter(DropTargetDragEvent dtde){

System.out.println("Drag Enter");

}

publicvoid dragExit(DropTargetEvent dte){

System.out.println("Drag Exit");

}

publicvoid dragOver(DropTargetDragEvent dtde){

System.out.println("Drag Over");

}

publicvoid dropActionChanged(DropTargetDragEvent dtde){

System.out.println("Drop Action Changed");

}

publicvoid drop(DropTargetDropEvent dtde){

try{

// O.k., get the dropped object and try to figure out what it is:

Transferable tr = dtde.getTransferable();

DataFlavor[] flavors = tr.getTransferDataFlavors();

for (int i = 0; i < flavors.length; i++){

System.out.println("Object type: " + flavors[i].getMimeType());

// Check for file lists specifically

if (flavors[i].isFlavorJavaFileListType()){

// Great! Accept the copy drops...

dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

ta.setText("Files dropped:\n\n");

// ... and add the list of file names to our text area!

java.util.List list = (java.util.List)tr.getTransferData(flavors[i]);

for (int j = 0; j < list.size(); j++){

ta.append(list.get(j) +"\n");

//****** TO DO HERE ******

// Add files to some transfer queue

//****************************

}

//****** TO DO HERE ******

// transfer the queue and put out some info about it

//****************************

// If we made it this far, everything worked.

dtde.dropComplete(true);

return;

}

}

// Error: The user must not have dropped a file list.

System.out.println("Drop failed: " + dtde);

dtde.rejectDrop();

}catch (Exception e){

e.printStackTrace();

dtde.rejectDrop();

}

}

publicstaticvoid main(String args[]){

new DropUploader();

}

}

Since I need to interact with the users filesystem, I guess I will have to be using jarsigner/keytool lateron.

Since I spent looking for something like that for quite a couple of days now, I promise to put this applet under some gpl-like public licence and make it available to the public on our corporate website together with a howto on its implementation, if we get this thing going.

:-)

cheers, matt

[7618 byte] By [mherrmanna] at [2007-11-27 10:31:58]
# 1

Not sure about the security-related issues of getting at the user's file system from an applet, but for the MULTIPART POST part, Apache httpclient is about a billion times easier than the Java API method.

jleecha at 2007-7-28 18:13:19 > top of Java-index,Security,Signed Applets...
# 2

ok, the jakarta classes where a huge help there. thanks a lot.

I have a first version working:

import java.awt.*;

import java.awt.dnd.*;

import java.awt.datatransfer.*;

import java.awt.event.*;

import java.io.*;

import java.util.*;

import javax.swing.*;

import org.apache.commons.httpclient.*;

import org.apache.commons.httpclient.methods.PostMethod;

import org.apache.commons.httpclient.methods.multipart.FilePart;

import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;

import org.apache.commons.httpclient.methods.multipart.Part;

import org.apache.commons.httpclient.params.HttpMethodParams;

public class DropUploader extends JApplet implements DropTargetListener {

public DropTarget dt;

private JTextArea ta;

private static final long serialVersionUID = 1;

public String targetURL = "http://localhost/testing/upload.php";

public void init() {

try {

duInit();

} catch(Exception e) {

e.printStackTrace();

}

}

public void duInit() throws Exception {

setSize(600,300);

ta = new JTextArea();

ta.setText("Drop files here.");

ta.setBackground(Color.white);

getContentPane().add(ta, BorderLayout.CENTER);

// Set up our text area to receive drops...

// This class will handle the drop events

dt = new DropTarget(ta, this);

setVisible(true);

// SetUp the MultipartPostMethod

}

public void dragEnter(DropTargetDragEvent dtde) {

System.out.println("Drag Enter");

}

public void dragExit(DropTargetEvent dte) {

System.out.println("Drag Exit");

}

public void dragOver(DropTargetDragEvent dtde) {

System.out.println("Drag Over");

}

public void dropActionChanged(DropTargetDragEvent dtde) {

System.out.println("Drop Action Changed");

}

public void drop(DropTargetDropEvent dtde) {

try {

// O.k., get the dropped object and try to figure out what it is:

Transferable tr = dtde.getTransferable();

DataFlavor[] flavors = tr.getTransferDataFlavors();

for (int i = 0; i < flavors.length; i++) {

System.out.println("Object type: " + flavors[i].getMimeType());

// Check for file lists specifically

if (flavors[i].isFlavorJavaFileListType()) {

// Great! Accept the copy drops...

dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);

ta.setText("Transferring Files ...\n\n");

// ... and add the list of file names to our text area!

java.util.List list = (java.util.List)tr.getTransferData(flavors[i]);

for (int j = 0; j < list.size(); j++) {

ta.append(list.get(j) + "\n");

File transferfile = new File(list.get(j).toString());

PostMethod filePost = new PostMethod(targetURL);

filePost.getParams().setBooleanParameter(HttpMethodParams.USE_EXPECT_CONTINUE, false);

try {

Part[] parts = {

new FilePart("userfile", transferfile)

};

filePost.setRequestEntity(

new MultipartRequestEntity(parts, filePost.getParams())

);

HttpClient client = new HttpClient();

client.getHttpConnectionManager().

getParams().setConnectionTimeout(5000);

int status = client.executeMethod(filePost);

if (status == HttpStatus.SC_OK) {

System.out.println(

list.get(j).toString() + " - Upload complete, response=" + filePost.getResponseBodyAsString()

);

ta.append("Transfer resulted: " + filePost.getResponseBodyAsString() + "\n\n");

} else {

System.out.println(

list.get(j).toString() + " - Upload failed, response=" + HttpStatus.getStatusText(status)

);

ta.append("Transfer resulted: " + HttpStatus.getStatusText(status) + "\n\n");

}

} catch (Exception ex) {

System.out.println("ERROR: " + ex.getClass().getName() + " "+ ex.getMessage());

ex.printStackTrace();

} finally {

filePost.releaseConnection();

}

}

ta.append("Transfer complete.\n\n");

ta.append("Drop files here.");

// If we made it this far, everything worked.

dtde.dropComplete(true);

return;

}

}

// Error: The user must not have dropped a file list.

System.out.println("Drop failed: " + dtde);

dtde.rejectDrop();

} catch (Exception e) {

e.printStackTrace();

dtde.rejectDrop();

}

}

public static void main(String args[]) {

new DropUploader();

}

}

you can check out the transfers with (e.g.) this simple php-script:

<?php

// http://localhost/testing/upload.php

$uploaddir = './uploads/';

$logfile = fopen('log.txt','w');

if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploaddir . $_FILES['userfile']['name'])) {

$content = "File is valid, and was successfully uploaded.\n";

fwrite($logfile, $content);

echo "OK";

} else {

$content = "Possible file upload attack!\n";

fwrite($logfile, $content);

echo "ERROR";

}

fclose($logfile);

?>

at the moment i invoke a single http connection for every file and i am lost trying to figure out how to to rewrite my code to use a single http connection and to submit multiple fileparts with it.

any suggestions?

furthermore it would be nice to show the user that something is going on while transferring and probably to disable the dropzone during transfers...

mherrmanna at 2007-7-28 18:13:19 > top of Java-index,Security,Signed Applets...
# 3

strangely enough i get the following error, when trying to load the applet in a browser:

java.lang.NoClassDefFoundError: org/apache/commons/httpclient/methods/RequestEntity

at java.lang.Class.getDeclaredConstructors0(Native Method)

at java.lang.Class.privateGetDeclaredConstructors(Unknown Source)

at java.lang.Class.getConstructor0(Unknown Source)

at java.lang.Class.newInstance0(Unknown Source)

at java.lang.Class.newInstance(Unknown Source)

at sun.applet.AppletPanel.createApplet(Unknown Source)

at sun.plugin.AppletViewer.createApplet(Unknown Source)

at sun.applet.AppletPanel.runLoader(Unknown Source)

at sun.applet.AppletPanel.run(Unknown Source)

at java.lang.Thread.run(Unknown Source)

i guess it has something top do with incorrect packaging. any help appreciated!

mherrmanna at 2007-7-28 18:13:19 > top of Java-index,Security,Signed Applets...
# 4

the above problem could be solved by the fatjar eclipse plugin.

i was hoping to get some further assistance here at sun dev forums and have to say that i was very dissapointed of the lack of response...

i now wrote an applet that allows file-uploads implementing a droptargetlistener interface. it uses the http post method for transfers.

the receiving systems can be designed in almost any language. i for myself use ruby on rails to store the received files to a database and tested it during development with the above php-receiver that would store the files to a directory.

the applet compiled to a size of about 380kb.

i then redesigned it (no jakarta httpclient anymore) and have a newer version that only uses 13kb.

so thanks to all for ... actually not much. (sadly enough)

cheers, matt

mherrmanna at 2007-7-28 18:13:19 > top of Java-index,Security,Signed Applets...