Twitter MultiPart Post using Apache HttpClient and Signpost

417 views
Skip to first unread message

Kerry

unread,
Sep 13, 2009, 5:30:46 PM9/13/09
to Signpost users
Hi all, here's a quick example of performing a multi-part post using
Apache HttpClient 4.x.

First off you'll need to download the latest packages from apache and
Signpost (HttpCore, HttpClient, apache James Mime4J, Signpost core,
and Signpost commonshttp).

We'll need to tweak some of HttpClient's defaults, like so:

public HttpParams getParams() {
//Tweak further as needed for your app
HttpParams params = new BasicHttpParams();
// set this to false, or else you'll get an
Expectation Failed: error
HttpProtocolParams.setUseExpectContinue(params, false);
return params;
}

Your actual method might look like this:

public void updateProfileBackground(User user, File file) {
try {
// Create a new consumer using the commons
implementation
OAuthConsumer consumer = new CommonsHttpOAuthConsumer(consumerKey,
consumerSecret, SignatureMethod.HMAC_SHA1);
consumer.setTokenWithSecret(getUserAccessToken(user),
getUserTokenSecret(user));
HttpPost uploadBackgroundPost = new HttpPost
(UPDATE_PROFILE_BACKGROUND_IMAGE_URL);

// The body of a multi-part post isn't needed
for the generation of the signature
consumer.sign(uploadBackgroundPost);

// only works in strict mode
MultipartEntity entity = new MultipartEntity
(HttpMultipartMode.STRICT);

//Twitter checks against supported file types
FileBody imageBody = new FileBody(file, "image/png");

entity.addPart("image", imageBody);
uploadBackgroundPost.setEntity(entity);
DefaultHttpClient httpClient = new DefaultHttpClient(getParams());

//If you're interested in the headers,
implement and add a request interceptor that prints them
httpClient.addRequestInterceptor(new PrintRequestInterceptor());

System.out.println(httpClient.execute(uploadBackgroundPost, new
BasicResponseHandler()));
} catch (Exception e) {
// do some proper exception handling here
e.printStackTrace();
}
}

Now oddly enough, if the multi-part form data is not being formatted
exactly to Twitter's preferences you won't get an error message, but
the background image will be set to a broken link! You might see the
image url being constructed as:
http://a3.twimg.com/profile_background_images/test.png instead of:
http://a3.twimg.com/profile_background_images/5463125/test.png
(Notice the missing folder)


Additionally here's an example if you're using Apache HttpClient2.x,
which uses a different API than HttpClient 4.x

First off you'll need to make your own adapter for the
MultipartPostMethod (or if you're adventurous HttpMethod more
generally).

Something along the lines of:

public class HttpClient2OAuthConsumer extends AbstractOAuthConsumer {
public HttpClient2OAuthConsumer(String consumerKey, String
consumerSecret,
SignatureMethod signatureMethod) {
super(consumerKey, consumerSecret, signatureMethod);
}

protected oauth.signpost.http.HttpRequest wrap(Object request) {
if (!(request instanceof MultipartPostMethod))
throw new IllegalArgumentException("This consumer expects requests
of type MultipartPostMethod");
else
return new HttpRequestAdapter((MultipartPostMethod) request);
}
}

And:
public class HttpRequestAdapter implements
oauth.signpost.http.HttpRequest {

private MultipartPostMethod method;

public HttpRequestAdapter(MultipartPostMethod method) {
this.method = method;
}
public String getContentType() {
Header contentHeader = method.getRequestHeader("Content-Type");
return (contentHeader==null) ? "" : contentHeader.getValue();
}

public String getHeader(String s) {
return method.getRequestHeader(s).getValue();
}

public InputStream getMessagePayload() throws IOException {
return new FileInputStream(method.getFileData());
}

public String getMethod() {
return method.getName();
}

public String getRequestUrl() {
try {
return method.getURI().getURI();
} catch (URIException e) {
e.printStackTrace();
}
return null;
}

public void setHeader(String s, String s1) {
method.setRequestHeader(s, s1);
}
}

This method will look very similar.

public void updateProfileBackgroundHttpCommons(User user, File
file) {
try {
HttpClient client = new HttpClient();
// make a new consumer with our own implementation
OAuthConsumer consumer = new HttpClient2OAuthConsumer(consumerKey,
consumerSecret, SignatureMethod.HMAC_SHA1);
consumer.setTokenWithSecret(getUserAccessToken(user),
getUserTokenSecret(user));
MultipartPostMethod mp = new MultipartPostMethod
(UPDATE_PROFILE_BACKGROUND_IMAGE_URL);

FilePart image = new FilePart("image", file, "image/png", null); //
again specify the type
//charset cannot be set to a type.
httpclient2.x mistakenly adds one to a filepart by default
image.setCharSet(null);
mp.addPart(image);
mp.setStrictMode(false);

consumer.sign(mp);
client.executeMethod(mp);
for(Header header : mp.getRequestHeaders()) {
System.out.println(header.toExternalForm());
}
System.out.println(mp.getResponseBodyAsString());

} catch (Exception e) {
// do some proper exception handling here
e.printStackTrace();
}
}

And that's it.

Hope this saves everyone a bit of headache trying to figure out what
bits to flip. Thanks Signpost!

matthias

unread,
Sep 14, 2009, 2:59:16 AM9/14/09
to Signpost users
Great stuff, thanks for sharing! Mind if I pull this into the wiki?
With mentioning you as the author of course.

Kerry

unread,
Sep 15, 2009, 12:50:27 PM9/15/09
to Signpost users
Yes, that would be fine.

matthias

unread,
Sep 15, 2009, 1:20:58 PM9/15/09
to Signpost users

Smelly Eddie

unread,
Oct 6, 2009, 8:03:01 PM10/6/09
to Signpost users
When I attempt to sign the post i get following error;

java.lang.IllegalArgumentException: The default consumer expects
requests of type java.net.HttpURLConnection

My code is;

URL url = new URL(
"http://api.netflix.com/users/"
+ userID
+ "/queues/disc");

Log.d("Oauth",url.toString());

HttpPost addMoviePost = new HttpPost(url.toString());

// The body of a multi-part post isn't needed for the generation
of the signature
NetFlix.oaconsumer.sign(addMoviePost); // error is caused here

Matthias Käppler

unread,
Oct 7, 2009, 2:45:09 AM10/7/09
to signpos...@googlegroups.com

signpost-core only signs HttpUrlConnection. To sign apache commons http requests, link the commonshttp jar and use CommonsHttpOAuthConsumer.

The documentation on the website contains example code and instructions.

--~--~---------~--~----~------------~-------~--~----~ You received this message because you are sub...

Smelly Eddie

unread,
Oct 7, 2009, 10:42:54 AM10/7/09
to Signpost users
What "site" is that?

I have been through all docs here; http://code.google.com/p/oauth-signpost/w/list

and did not see such a thing.

On Oct 7, 2:45 am, Matthias Käppler <m.kaepp...@googlemail.com> wrote:
> signpost-core only signs HttpUrlConnection. To sign apache commons http
> requests, link the commonshttp jar and use CommonsHttpOAuthConsumer.
>
> The documentation on the website contains example code and instructions.
>

Smelly Eddie

unread,
Oct 7, 2009, 10:43:31 AM10/7/09
to Signpost users
Sorry, found it;
http://code.google.com/p/oauth-signpost/wiki/ApacheCommonsHttp4

On Oct 7, 2:45 am, Matthias Käppler <m.kaepp...@googlemail.com> wrote:
> signpost-core only signs HttpUrlConnection. To sign apache commons http
> requests, link the commonshttp jar and use CommonsHttpOAuthConsumer.
>
> The documentation on the website contains example code and instructions.
>
Reply all
Reply to author
Forward
0 new messages