Captain Spam (captainspam) wrote in lj_dev,
Captain Spam
captainspam
lj_dev

  • Mood:

FotoBilder and HttpComponents 4 (with MultipartEntity)

At one point, I wired together a somewhat simple FotoBilder client that I never announced on lj_dev. Oops. Long story short, it was written in Java, it used the Apache HttpComponents 3.x libraries, and it used multipart-MIME when it came to preparing images for upload and doing the actual uploading itself. And all was well. Relatively simple, but well. HttpComponents 3.x worked perfectly.

Then along comes HttpComponents 4.x, and they yoinked the multipart-MIME support from it, apparently. Instead, they tie into, I believe, HttpMime's MultipartEntity. And honestly, that shouldn't be much of a problem, either, as it seems fairly simple to use in-code. And, as a matter of fact, it DOES work, right up to the point where I give it any sort of binary data via an InputStreamBody.

At that point, for some reason, FotoBilder throws a 201 error back at me, "Invalid request: Couldn't parse upload". Now, as far as I can tell, I have everything set up right, and any multipart message to FotoBilder WITHOUT binary data is working, and the server isn't throwing a protocol-level error at me (that is, it's definitely FotoBilder complaining that it can't parse my upload, not Apache claiming I'm violating protocol), so I'm a bit stumped. Does anyone have experience using HttpComponents 4.x in Java and knows how MultipartEntity issues its data? Anything I'm missing, or is it something in FotoBilder?

Oh, and yes, I do somewhat need to switch to HttpComponents 4.x for a project I'm recycling this code into, so staying behind isn't an option.

Alternatively, is the XML FotoBilder interface made yet? Might be simpler. :-)

Behind the cut is the code in question...

import java.net.*;
import java.io.*;
import java.util.*;
import org.apache.http.HttpResponse;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.*;

/**
* A MultipartPoster handles simple multipart POST requests. It
* mainly provides a bunch of convenience methods to the Jakarta Commons
* HttpClient classes. Note that it breaks a lot, such as authentication,
* cookies, and other things that FotoFoo doesn't care about. Don't use this
* for complex stuff.
*
* @author Nicholas Killewald
*/
public class MultipartPoster {

private class Part {
public String name;
public ContentBody content;

public Part(String name, ContentBody content) {
this.name = name;
this.content = content;
}
}

/** The client object. */
private HttpClient client;
/** The response of choice. */
private HttpResponse response;
/** If we ever have a use for the HttpPost object, here it is. */
private HttpPost request;
/** Bucket o' parts (see above) */
private LinkedList parts;

/**
* Creates a new instance of MultipartPoster using the specified PostMethod.
* It is assumed that this PostMethod is fresh from constructing.
*
* @param request PostMethod to make a multipart post out of
*/
public MultipartPoster(HttpPost request) {
// Make a new client...
client = new DefaultHttpClient();

// Assign the method...
this.request = request;

// And get ready with an empty bucket o' parts.
parts = new LinkedList();
}

/**
* Creates a new instance of MultipartPoster using the specified String,
* which in turn should be a URL to open up.
*
* @param url String to make a URL to connect to
*/
public MultipartPoster(String url) {
this(new HttpPost(url));
}

/**
* Creates a new instance of MultipartPoster using the specified URL.
* Seriously, make sure it's a URL to an HTTP connection. Or maybe an
* HTTPS connection, since it's a descendant.
*
* @param url URL to connect to
*/
public MultipartPoster(URL url) {
this(url.toString());
}

/**
* Returns the HttpResponse object used by this. Note that this should
* only be called after the connection is posted.
*
* @return the HttpResponse object generated by this MultipartPoster
*/
public HttpResponse getResponse() {
return response;
}

/**
* Returns the HttpPost that came from this. You never know, you might
* come up with a use for it.
*
* @return the HttpPost object used by this MultipartPoster
*/
public HttpPost getRequest() {
return request;
}

/**
* Adds the given name and value to the connection.
*
* @param name name of parameter
* @param value value of parameter
*/
public void addParameter(String name, String value) {
try {
parts.add(new Part(name, new StringBody(value)));
} catch (Exception e) {
// Do something or another
}
}

/**
* Adds the given File to the connection.
*
* @param file File to add
* @throws FileNotFoundException the file could not be found
*/
public void addParameter(File file) throws FileNotFoundException {
parts.add(new Part(file.getName(), new FileBody(file)));
}

/**
* Adds the given byte array to the connection as a file. It will have the
* given parameter name and the given filename.
*
* TODO: This appears to not be doing things right.
*
* @param name name of parameter
* @param filename name of file
* @param input byte array of data to throw in
*/
public void addParameter(String name, String filename, byte[] input) {
// Life's so much easier with InputStreamBody!
parts.add(new Part(name, new InputStreamBody(new ByteArrayInputStream(input), filename)));
}

/**
* Adds the given InputStream to the connection as a file. It will have the
* given parameter name and the given filename.
*
* @param name name of parameter
* @param filename name of file
* @param is InputStream to read data from
* @throws IOException some manner of IOException tomfoolery was thrown
*/
public void addParameter(String name, String filename, InputStream is) throws IOException {
// This version is simpler, as we have a direct InputStreamBody.
parts.add(new Part(name, new InputStreamBody(is, filename)));
}

/**
* Starts the post in motion. Once this returns, the PostMethod should be
* ready to be read from for any sort of action needed afterward.
*/
public void go() throws IOException {
// Now, the parts are all just shoved into the big ol' LinkedHashMap.
// Let's pull it out to an array and shove it into a MultipartEntity.
MultipartEntity requestContent = new MultipartEntity();
for (Part p : parts) {
requestContent.addPart(p.name, p.content);
}

request.setEntity(requestContent);
response = client.execute(request);
}

}


The response from go() is then fed into a parser. Said parser is what's telling me that FB is returning a 201 error. I'm afraid I don't have a capture of the exact data being sent.
Tags: client, client: fotobilder, code: java
Subscribe
  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

  • 8 comments