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.DefaultHttpC lient;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.mime.MultipartEnt ity;
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.