Saturday, June 20, 2009

JavaFX http file upload

I have been fiddling around to find an easy way to upload files with a post http request. This doesn't come out of the box with JavaFX 1.2. With a simple subclass of HttpRequest however this is possible:

Consider the use case of having the user to select one or more files to upload :

// total amount of bytes to upload
var uploadFileSize = 0;

// total amount uploaded bytes
var uploadedBytes = 0;

// a file chooser to select the files to upload
def fc = JFileChooser{};

// the http request object to use for a post
var uploadRequest:HttpRequest;

// the url to post to
def uploadURL = "http://localhost:8080/alexandria/wsrs/document";

// more than one file can be selected
fc.setMultiSelectionEnabled(true);

// the user did select one or more files
if ( fc.showOpenDialog(null) == JFileChooser.APPROVE_OPTION ) {

// compute total bytes to upload (approximation)
uploadFileSize = 0;
for ( file in fc.getSelectedFiles() ) {
uploadFileSize += file.length();
}

// kick off the upload
uploadedBytes = 0;
progressDialog.visible = false;

// initiate the upload request to the server
uploadRequest = FileUploadHTTPRequest {
location: uploadURL
files: fc.getSelectedFiles()

onWritten:function(bytes: Long) {
uploadedBytes = bytes;
}

onDone: function() {
println("Upload finished")
}

onResponseCode: function(code:Integer) {
println("onResponseCode - responseCode: {code}")
}

onResponseMessage: function(msg:String) {
println("onResponseMessage - responseMessage: {msg}")
}
}

uploadRequest.start();

}

This is the source code for the upload http request

import javafx.io.http.HttpRequest;
import java.io.File;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import javafx.io.http.HttpHeader;
import javafx.date.DateTime;
import javafx.data.Pair;

/**
* HttpRequest able to upload files to a given url, using Pair objects
* to send fields.
*
* @author jan
*/

def PREFIX = "--";

def NEWLINE = "\r\n";

def BOUNDARY = createBoundary();

def CONTENT_TYPE = "multipart/form-data; boundary={BOUNDARY}";

def NAME = "upfile";

function createBoundary() :String {
def now = DateTime {};
return "{Long.toHexString(now.instant)}";
}


public class FileUploadHTTPRequest extends HttpRequest {

// A sequence of files to upload
public-init var files:File[];

// Fields to upload together with the request
public-init var fields:Pair[];

postinit{

// we always do a post
method = HttpRequest.POST;

// override the onOutput
onOutput = writeFiles;

// add the headers we need
insert HttpHeader {
name:HttpHeader.ACCEPT
value: "*/*"
} into headers;
insert HttpHeader {
name:HttpHeader.CONTENT_TYPE
value: CONTENT_TYPE
} into headers;
insert HttpHeader {
name:HttpHeader.CONNECTION
value: "keep-alive"
} into headers;
insert HttpHeader {
name:HttpHeader.CACHE_CONTROL
value: "no-cache"
} into headers;
insert HttpHeader {
name:HttpHeader.ACCEPT_CHARSET
value: "ISO-8859-1,utf-8;q=0.7,*;q=0.7"
} into headers;
}

function writeFiles( output:OutputStream ) {
try {
// send the files
if ( files != null and sizeof files > 0 ) {
for ( file in files ) {
// leading characters
output.write(PREFIX.getBytes());
output.write(BOUNDARY.getBytes());
output.write(NEWLINE.getBytes());
output.write("Content-Disposition: form-data; name=\"{NAME}\"; filename=\"{file.getName()}\"".getBytes());
output.write(NEWLINE.getBytes());
output.write("Content-Type: application/octet-stream".getBytes());
output.write(NEWLINE.getBytes());
output.write(NEWLINE.getBytes());
// write the file's content
var input = new BufferedInputStream( new FileInputStream( file ) );
IOUtils.copy(input, output);
input.close();
// end of file
output.write(NEWLINE.getBytes());
output.flush();
}
output.write(NEWLINE.getBytes());
}

// write the fields
if ( fields != null and sizeof fields > 0 ) {
for ( field:Pair in fields ) {
output.write(PREFIX.getBytes());
output.write(BOUNDARY.getBytes());
output.write(NEWLINE.getBytes());
// write content header
output.write("Content-Disposition: form-data; name=\"{field.name}\"".getBytes());
output.write(NEWLINE.getBytes());
output.write(NEWLINE.getBytes());
// write content
output.write("{field.value}".getBytes());
output.write(NEWLINE.getBytes());
output.flush();
}
}
output.write(PREFIX.getBytes());
output.write(BOUNDARY.getBytes());
output.write(PREFIX.getBytes());
output.write(NEWLINE.getBytes());

} finally {
output.flush();
output.close();
}
}
}

No comments:

Post a Comment