Last night I decided to knock out a quick app to test how straightforwardly one can upload twitter images. With the exception of one hiccup:
http://code.google.com/p/twitter-api/issues/detail?id=697
it went very smoothly.
Essentially you need to introduce two libraries into your project that are absent from Android:
I used versions 4.0-beta2 and 0.5 respectively.
Here’s my very basic implementation:
//sample method for uploading a user profile image to twitter
int uploadImage(InputStream in, String type, String fileName, Credentials credentials) throws IOException {
try {
//convert the input stream into a byte array - this is just a convenient way
InputStreamEntity ise = new InputStreamEntity(in, -1L);
byte[] bytes = EntityUtils.toByteArray(ise);
//establish the credentials
DefaultHttpClient client = new DefaultHttpClient();
client.getCredentialsProvider().setCredentials(new AuthScope("twitter.com", 80), credentials);
//create the multipart entity
MultipartEntity entity = new MultipartEntity();
ByteArrayBody body = new ByteArrayBody(bytes, type, fileName);
entity.addPart("image", body);
//assign the entity to the request and disable the Expect header
//see http://code.google.com/p/twitter-api/issues/detail?id=697
HttpPost request = new HttpPost("http://twitter.com/account/update_profile_image.xml");
request.setEntity(entity);
request.getParams().setBooleanParameter( "http.protocol.expect-continue", false );
//execute the request and return the response
HttpResponse response = null;
try {
response = client.execute(request);
} finally {
//exhaust the response
if (response != null) {
HttpEntity re = response.getEntity();
if (re != null) re.consumeContent();
}
}
return response.getStatusLine().getStatusCode();
} finally {
try {
in.close();
} catch (IOException e) {
/* ignored */
}
}
}
I haven’t investigated properly, but disabling the “expect-continue” feature appears to cause the request entity to be read twice. So the method above depends on a helper class that provides a repeatable entity.
private class ByteArrayBody extends AbstractContentBody {
private final byte[] bytes;
private final String fileName;
public ByteArrayBody(byte[] bytes, String mimeType, String fileName) {
super(mimeType);
this.bytes = bytes;
this.fileName = fileName;
}
@Override
public String getFilename() {
return fileName;
}
@Override
public void writeTo(OutputStream out, int mode) throws IOException, MimeException {
out.write(bytes);
}
@Override
public String getCharset() {
return null;
}
@Override
public long getContentLength() {
return bytes.length;
}
@Override
public String getTransferEncoding() {
return MIME.ENC_BINARY;
}
}
The Java code in this post is in the public domain.
I’ve bowed to the inevitable and now have a twitter account. Being on the geeky end of the human spectrum, the first thing I wanted to do was write a some Java code that will push new profile images.
I’m running along nicely, and the first problem I hit comes as I finish my coding and start testing. Twitter responds with:
The expectation given in the Expect request-header field could not be met by this server.
The client sent
Expect: 100-Continue
but we only allow the 100-continue expectation
That such a widely used service can’t stick to one of the plainest elements of the HTTP 1.1 specification is depressing.
Comparison of expectation values is case-insensitive for unquoted tokens (including the 100-continue token), and is case-sensitive for quoted-string expectation-extensions.
But what is worse is that the Apache HTTP library I’m using doesn’t allow this header to be amended in any way — it gets magically added at the point at which the request is made so there’s no opportunity to remove it — and something as simple as this multipart image upload becomes a trial…