Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie

Post Requests with Java

Options
  • 15-08-2011 10:56am
    #1
    Registered Users Posts: 3,805 ✭✭✭


    Hello folks, looking for a bit of help with something I'm working on but can't seem to find a solution to. I'm trying to upload an image to tumblr from java - and unfortunately no matter what I try it returns a 403 forbidden error - although unfortunately the cause of this is not as simple as a typo in my password or username :( I've been looking at how other people have tried it, but I can't find a successful example of how to do it. Btw Post requests to send text content to tumblr work perfectly, so I'm pretty certain the problem is related to the image encoding.

    Does anybody have any ideas, or perhaps can spot any glaring errors in my code? I've been playing around with it so much I don't know what to make of it anymore myself! Thanks in advance.

    //edit: here's the tumblr api page: http://www.tumblr.com/docs/en/api#api_write

    Here's the summarised version of my code:
    import javax.imageio.*;
    import java.awt.image.*;
    import java.io.OutputStreamWriter;
    import java.io.IOException;
    import java.net.HttpURLConnection;
    import java.net.URL;
    import java.net.URLEncoder;
    import java.net.HttpURLConnection;
    
    public void postImage(String srcImage) {
      BufferedImage imageToWrite = null;
      String username = "email";
      String password = "password";
      String enc = "UTF-8";  //encoding type
      String type = "photo";  //type of tumblr post
      String title = "test";  //the title of the post
    
      try {
        
        
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        
        //write the file into the ByteArrayOutputStream stream for uploading
        File file = new File(srcImage);
        imageToWrite = ImageIO.read(file);
        ImageIO.write(imageToWrite, "jpg", stream);
    
        //the Tumblr api requests at least an email, password, post-type, image-source, and title to make the post
        //encoding the url
        String data = URLEncoder.encode("email", enc) + "=" + URLEncoder.encode(username, enc);
        data += "&" + URLEncoder.encode("password", enc) + "=" + URLEncoder.encode(password, enc);
        data += "&" + URLEncoder.encode("type", enc) + "=" + URLEncoder.encode(type, enc);
        data += "&" + URLEncoder.encode("data", enc) + "=" + URLEncoder.encode(stream.toString(), enc);
        data += "&" + URLEncoder.encode("title", enc) + "=" + URLEncoder.encode(title, enc); 
        
        //make a connection, set http parameters
        URL tumblr = new URL("http://www.tumblr.com/api/write");
        HttpURLConnection http = (HttpURLConnection) tumblr.openConnection();
        http.setDoOutput(true);
        http.setRequestMethod("POST");
        http.setRequestProperty("Content-Type", "multipart/form-data"); //tumblr needs multipart/form-data
        
        OutputStreamWriter out = new OutputStreamWriter(http.getOutputStream());
        
        //connect and send the url string data
        http.connect();
        out.write(data);
        out.flush();
        System.out.println(http.getResponseCode());
        System.out.println(http.getResponseMessage());
        out.close();
      }
      catch(IOException e) {
        e.printStackTrace();
      }
    }
    


Comments

  • Registered Users Posts: 2,800 ✭✭✭voxpop


    I dont think the content type should be multipart, because you are not sending a multipart post

    from the docs for fileupload either -
    "multipart/form-data method, like a file upload box in a web form."
    or
    "Normal POST method, in which the file's entire binary contents are URL-encoded like any other POST variable."

    You are encoding the binary contents.

    If you want to do a multipart post, id look at something like the apache commons httpclient - it'll save alot of headaches


  • Registered Users Posts: 385 ✭✭EoghanConway


    Not an expert on this, but here we go...
    The Write API is a very simple HTTP interface. To create a post, send a POST request to http://www.tumblr.com/api/write with the following parameters

    I would take it that this means to set http request properties eg. http.setRequestProperty("password", "mypassword")

    This would explain why you are getting 403

    sample php code seems to confirm this

    Edit
    Actually, php is doing something else completely. According to the API there are two different ways of doing this: the first is to create a multipart form data message where the variables are separated by boundaries. The second is to encode the file as part of a URL-encoded query string (e.g. foo=bar&baz=boom&cow=milk&php=hypertext+processor)

    The supplied php demo gives an example of the second, the way the op eventually did it is an example of the first


  • Registered Users Posts: 3,805 ✭✭✭Setun


    I think the only way I'm going to get this to work is to use the apache commons client as you suggested, I haven't seen a working example of posting an image to tumblr yet - it seems to be left out of any of the java libraries that I have seen so it's obviously not as simple as I had hoped.

    Thanks for the suggestion EoghanConway, but that doesn't work either. Posting an image from a url, or writing text to tumblr is ok, I only get the 403 when I go to upload a local file - so it has to be with how the image is encoded/sent to tumblr. Quite frustrating! :/


  • Registered Users Posts: 1,992 ✭✭✭lynchie


    As a quick test Im getting a 403 even if I exclude the data parameter. From their docs, a 403 is returned for authentication issues. I dont have an account to test with but try exclude the data param with a valid username / password and see what error you get back.


  • Registered Users Posts: 3,805 ✭✭✭Setun


    Hey - thanks for the reply.

    Excluding the image data returns a 403 - the problem must be related to the "content-type". It's obviously not "multipart/form-data" as pointed out above, as it is url-encoded string-converted byte data, so any ideas as to what should I set it as for it to upload successfully?

    Also, leaving out the "content-type", and changing the post type to "regular" (regular is a text only post) instead of "photo" uploads successfully - even with the data parameter left in - but obviously leaves out the image from the post.


  • Advertisement
  • Registered Users Posts: 2,800 ✭✭✭voxpop


    Can you leave out the content-type and do everything else the same - the api suggests that you can upload a file using a regular POST - just that its not very efficient


  • Registered Users Posts: 3,805 ✭✭✭Setun


    Leaving out the content type entirely returns a 400 - bad request error.

    Think I'll have to go back to the drawing board on this one perhaps. Maybe a multipart upload will work, although I've never really used the apache httpclient before, so it will be a bit of an adventure.


  • Registered Users Posts: 2,800 ✭✭✭voxpop


    You should get a plain text error - according to the api.


    Try content-type of application/x-www-form-urlencoded . You might need the content-length aswell, thought I think its optional mostly.

    The httpclient is pretty easy to use, there are a few examples on the net for creating mutlipart posts in about 3 lines


  • Registered Users Posts: 3,805 ✭✭✭Setun


    Yeah as far as I know the content-length is an optional parameter, but adding it in doesn't change the error message, neither does setting the content-type to "application/x-www-form-urlencoded".

    Right, off to look at the apache commons stuff then!

    Thanks muchly for all the suggestions so far, really appreciate it :)


  • Registered Users Posts: 1,311 ✭✭✭Procasinator


    It probably isn't it, but could title be an invalid argument for photo uploads?

    If you look at Post Types in the API, the regular, conversation and video post types mention title. "photo" and the rest don't. photo does have caption, which is probably the correct field to use.

    It could be that Tumblr rejects API calls that don't fit into the defined structure (i.e. have undefined arguments).


  • Advertisement
  • Registered Users Posts: 3,805 ✭✭✭Setun


    It probably isn't it, but could title be an invalid argument for photo uploads?

    If you look at Post Types in the API, the regular, conversation and video post types mention title. "photo" and the rest don't. photo does have caption, which is probably the correct field to use.

    It could be that Tumblr rejects API calls that don't fit into the defined structure (i.e. have undefined arguments).
    I wish it was that! I think what the issue is relates to the fact that I'm url-encoding the whole binary contents of the file, instead of making a multipart/form-data upload as recommended in their api. Perhaps they stopped supporting uploading files in this manner? Or perhaps I'm leaving out some integral http request property? Not sure. In any case, it's the only thing that I can't manage to get working, and the code has essentially lost all meaning to me at this stage I've spent so long staring at it :P


  • Registered Users Posts: 1,311 ✭✭✭Procasinator


    I suppose rule out the encoding from stream.toString() is causing a problem by using stream.toString(enc).


  • Registered Users Posts: 3,805 ✭✭✭Setun


    Tried, but doesn't change the error message. Maybe I'll message tumblr support and ask if they still support binary-content uploads? They could have removed support after the migration to api v2 (although api v1 still works fine for everything else).


  • Registered Users Posts: 3,805 ✭✭✭Setun


    Just got the dastard to work using the apache commons httpclient - it sounds funny saying this now but it was actually not that difficult. Sometimes the best thing to do with a stuck coding project is to try something completely different I think.

    Thanks to voxpop for the suggestion! One thing I noticed though - the httpclient package is now deprecated - it's been somewhat encapsulated within the http-components package now and the structure seems to be a bit different.


  • Registered Users Posts: 3,805 ✭✭✭Setun


    Just got the dastard to work using the apache commons httpclient - it sounds funny saying this now but it was actually not that difficult. Sometimes the best thing to do with a stuck coding project is to try something completely different I think.Thanks to voxpop for the suggestion!

    One thing I noticed though - the httpclient package is now deprecated - it's been somewhat encapsulated within the http-components package now and the structure seems to be a bit different.


  • Registered Users Posts: 3,805 ✭✭✭Setun


    A mod can mark this as fixed if necessary. Here's the code snippet in case somebody in the far future needs it:
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.HttpException;
    import org.apache.commons.httpclient.methods.MultipartPostMethod;
    import org.apache.commons.httpclient.methods.multipart.FilePart;
    import java.io.IOException;
    
    public void postImageData(String filesrc) {
      try {
        HttpClient client = new HttpClient();
    
        String weblintURL = "http://tumblr.com/api/write";
        MultipartPostMethod method = new MultipartPostMethod(weblintURL);
    
        File file = new File(filesrc);
    
        method.addParameter("email", "[email]");
        method.addParameter("password", "[password]");
        method.addParameter("type", "photo");
        method.addParameter("data", file);
        method.addParameter("caption", "[caption]");
        method.addParameter("tags", "[tag]");
    
        // Execute and print response
        client.executeMethod( method );
        String response = method.getResponseBodyAsString( );
        System.out.println(response);
        method.releaseConnection( );
      }
      catch(IOException e) {
        e.printStackTrace();
      }
    }
    


Advertisement