2

I need to post a image to Twitter. I have integrated Twitter in my app. I need to tweet the image as such not as an URL link. I don't want to use TwitPic.

I used the following code to create the multipart entity. It give 404 error.

Bitmap bm = null;
                String encodedImage = "";
                try {

                    URL aURL = new URL("http://50.57.227.117/blacksheep/uploaded/Detailed_images/961314275649aladdins.jpg");
                    URLConnection conn = aURL.openConnection();
                    conn.connect();
                    InputStream is = conn.getInputStream();
                    BufferedInputStream bis = new BufferedInputStream(is, 8192);
                    bm = BitmapFactory.decodeStream(bis);
                    bis.close();
                    is.close();
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
                     imageBytes = baos.toByteArray();
                     encodedImage = Base64.encodeToString(imageBytes, Base64.DEFAULT);
                     Log.v("encodedImage >>",encodedImage); 

                } catch (MalformedURLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }

                HttpClient httpClient = new DefaultHttpClient();
                HttpPost postRequest = new HttpPost(
                        "https://api.twitter.com/1.1/statuses/update_with_media.json");
               ByteArrayBody bab = new ByteArrayBody(imageBytes, "forest.jpg");

                MultipartEntity reqEntity = new MultipartEntity(
                        HttpMultipartMode.BROWSER_COMPATIBLE);
              reqEntity.addPart("media", bab);
                reqEntity.addPart("status", new StringBody("test image"));
                postRequest.setEntity(reqEntity);
                HttpResponse response = httpClient.execute(postRequest);
                BufferedReader reader = new BufferedReader(new InputStreamReader(
                        response.getEntity().getContent(), "UTF-8"));
                String sResponse;
                StringBuilder s = new StringBuilder();

                while ((sResponse = reader.readLine()) != null) {
                    s = s.append(sResponse);
                }
                System.out.println("Response: " + s);
            // Update status
            //twitter4j.Status response = twitter.updateStatus(encodedImage);
        //  twitter4j.Status response1 = twitter.updateStatus(status);

            //Log.d("Status", "> " + response1.getText());
        } catch (TwitterException e) {
            // Error in updating status
            Log.d("Twitter Update Error", e.getMessage());
        }
Manikandan
  • 1,479
  • 6
  • 48
  • 89
  • How have you integrated Twitter into your app? Are you using any twitter-specific libraries? Best. – Tom May 24 '13 at 11:31
  • yes, I have integrated Twitter in my app. – Manikandan May 24 '13 at 12:43
  • Sorry if I've not been clear, *how* have you integrated twitter into your app? Anyhow, looks like you've posted more, and you're using Twitter4j – Tom May 24 '13 at 13:10
  • Thanks for sharing this code. So you're using the deprecated apache http client to make this post to twitter, and your problem status is printed `System.out.println("Response: " + s);`. but I don't see here how you can be sending up the authentication for the user using it? Why can't you do this within twitter4j, as it must surely handle all the oauth? You may have to fix this before it'll work. – Tom May 24 '13 at 13:16
  • Here is a tutorial: http://sholtz9421.wordpress.com/2011/10/29/using-update_with_media-with-twitter4j/ . If you don't get on with twitter4j, actually twitter's api is really intuitive, it's easy to write your own client and let something like scribe handle the oauth! Best wishes! – Tom May 24 '13 at 13:20
  • thanks for the response. from the link you referred, i didn't get this line. 1. Find the “StatusMethods” class in the “twitter4j.api” package, and add the following interface”: I opened the twitter4j.api in referenced library, but I can't include the interface they said in that blog. – Manikandan May 24 '13 at 14:25
  • OK, that's a shame. Sounds like you have a different twitter4j version. So, let's give up on trying to add it to your current version. Get the latest version of twitter4j http://twitter4j.org/en/ then I'll write an answer! Best, Tom. – Tom May 24 '13 at 14:36
  • I'm using twitter4j-core-3.0.3.jar. – Manikandan May 24 '13 at 14:39

2 Answers2

5

First attempt:

So it seemed like you were using Apache's old http client to make a request external to the library you were using to help integrate with twitter, twitter4j. I assumed you were using a version prior to 3.03 which is latest, and didn't want you to upgrade. You see, update_with_media is quite new, so I didn't think your version had implemented it.

The problem with what you were doing is that twitter uses oauth for authentication. So you'd need to "sign" a request with the access token you'd obtained. Twitter4j, AFAIK, does this for you. You can't use a seperate client to make some calls without reference to your nice helper library without breaking authentication.

The endpoint, ../update_with_media is defined to update a status for the currently authenticating user. I suspect that, since there was no access token and no user in your request, that endpoint doesn't even make sense, so twitter were interpreting it as a 404 (not found) rather than a 401 (unauthorized)- funny.

So the first attempt was not to require you to upgrade to twitter4j. It's a pain to upgrade sometimes! Instead, you can hack with the library as is detailed with this blog. But that wasn't easy as the libraries were different.

So, something else we could try, if you really wanted to make a seperate request to twitter4j, was to actually do the signing, perhaps using scribe to make it easier.... roughly:

        final OAuthService myTwitterService = TwitterClient.getTwitterClient().getService();
        final OAuthRequest aNiceOAuthRequest = new org.scribe.model.OAuthRequest(
                YOURPOST, THATURL);

etc.

Second attempt:

But let's not do all this- turns out you had the latest version of twitter4j anyway. Sorry for going down a cul-de-sac first- I shouldn't have assumed, but I've included the above for help for anybody else should they need it.

It turns out the latest version has implemented this endpoint- documentation here. Except it takes a StatusUpdate object instead. So you want to do something like:

final StatusUpdate statusUpdate = new StatusUpdate("Hallee hallo my status java.lang.String here...");
       // now do as you did until:
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
                bm.compress(Bitmap.CompressFormat.PNG, 100, baos);
                 imageBytes = baos.toByteArray();
                 encodedImage = Base64.encodeToString(imageBytes, Base64.DEFAULT);
       // then flip the stream
       byte[] myTwitterUploadBytes = bos.toByteArray();
       ByteArrayInputStream bis = new ByteArrayInputStream(myTwitterUploadBytes);
       // doo and double check your encoding etc, similar to in your question..
       statusUpdate.setMedia("give me a java.lang.String name", bis);
       // then continue just using twitter for j- update status as you would
       //... get twitter etc...
       //
       twitter4j.Status response = twitter.updateStatus(statusUpdate);

Haven't currently got a box on me to test- should be about right. If it still gives 404s, what is the error code in the response? Are you authenticated?

If that doesn't work, we can try some of the above too as a back up.

Hope this helps,

best,

Tom.

Community
  • 1
  • 1
Tom
  • 1,773
  • 15
  • 23
  • Apprently you were getting a 403- that can also happen if the name of the file you want to link to plus your status exceeds the limit of the number of characters per tweet: see https://dev.twitter.com/docs/api/1.1/post/statuses/update_with_media – Tom May 24 '13 at 15:47
  • i already tried the second attempt. It tweets as an tiny url. I want to tweet image as such, not as url. – Manikandan May 24 '13 at 15:50
  • Great- does it not appear when you click the tweet? – Tom May 24 '13 at 15:53
  • Yes, it appears, i don't want like that. I want to post the image directly. – Manikandan May 24 '13 at 17:03
  • Can you give me an example on twitter of what you mean? AFAIK, you can't *embed* an image into a tweet... you have a link and when you click the tweet "expands". There's always ASCII art... Either way, what you're doing sounds cool and I wish you best of luck indeed! – Tom May 24 '13 at 17:20
  • I come to know, that we can't tweet the image as such. thanks for your efforts to answer my question. – Manikandan May 24 '13 at 18:14
  • Honestly, the second attempt shown here with twitter4j 3.0.3 lib is the only working solution I found in the internet.. Thanks a ton.. saved my day .. – Andro Selva Jun 29 '13 at 06:13
  • As of at least 4.0.3 it's now quite simple to upload an embedded image in a tweet. – Lawrence Dol Jun 11 '15 at 03:09
  • Hi Lawrence. Yeah; this is now out of date. Perhaps worth making the question / answer version specific for folks coming in off google? – Tom Jun 11 '15 at 07:40
2

Using 4.0.3 (perhaps earlier) it's very simple to embed images in a tweet with twitter4j:

Twitter                         twtobj;
StatusUpdate                    stsupd;
Status                          stsres;

twtobj=twitterFactory.getInstance();
twtobj.setOAuthConsumer(csmkey,csmsec);
twtobj.setOAuthAccessToken(new AccessToken(acstkn,acssec));

stsupd=new StatusUpdate(msgtxt);
if(medurls.length>0) {
    long[]                      medidns=new long[medurls.length];

    for(int xa=0; xa<medurls.length; xa++) {
        String                  medurl=Util.resolveRelativeUrl(medurls[xa]);
        InputStream             imgstm=null;

        try {
            imgstm=new URL(medurl).openConnection().getInputStream();
            medidns[xa]=twtobj.uploadMedia(medurl,imgstm).getMediaId();                     // this actually uploads the image to Twitter at this point
            }
        catch(MalformedURLException thr) { throw new ShfFail(Fail.IMAGE_URL ,"The media URL is not valid: " +medurl+" ("+thr.getMessage()+")"); }
        catch(IOException           thr) { throw new ShfFail(Fail.IMAGE_READ,"The media could not be read: "+medurl+" ("+thr.getMessage()+")"); }
        finally                          { GenUtil.close(imgstm); }
        }
    stsupd.setMediaIds(medidns);
    }
stsres=twtobj.updateStatus(stsupd);

Note that up to 4 images, or 1 animated GIF, or 1 video are allowed, as of 2015-06-10.

Note also that I am capturlng the image streams to close them explicitly in the outer block (not shown). This may be unnecessary, but I can't find positive confirmation of that.

If anyone cares, resolveRelativeUrls is a convenience to allow a relative path to be resolved as a file URL from the current folder:

static public String resolveRelativeUrl(String url) {
    if(!TextUtil.stringCT(url,"://")) {
        url=new File(url).getAbsoluteFile().toURI().toString();
        }
    return url;
    }

The utility method stringCT is case-insensitive contains.

Lawrence Dol
  • 63,018
  • 25
  • 139
  • 189