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]

# 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...