Latest Tweets

 

Uploading Twitter profile images via Android

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:

  • httpmime.jar
  • apache-mime4j.jar

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.

Twitter API, what do you Expect?

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…