Posting images to twitter in Node.js using Oauth

Problem

I'm trying to post images to Twitter using the Oauth module. Here is what I have:

It throws a 403 error, I know im doing something wrong with how I add the media to the post but Im just not sure where to go from here.

var https = require('https');
var OAuth= require('oauth').OAuth;
var keys = require('./twitterkeys');
var twitterer = new OAuth(
           "https://api.twitter.com/oauth/request_token",
           "https://api.twitter.com/oauth/access_token",
           keys.consumerKey,
           keys.consumerSecret,
           "1.0",
           null,
           "HMAC-SHA1"
          );


var params = {
    status : "Tiger!",
    media : [("data:" + mimeType + ";base64,") + fs.readFileSync(path,'base64')]
};

//function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback)
twitterer.post("https://upload.twitter.com/1/statuses/update_with_media.json",
           keys.token, keys.secret, params, "multipart/form-data",
           function (error, data, response2) {
           if(error){
               console.log('Error: Something is wrong.\n'+JSON.stringify(error)+'\n');

           }else{
               console.log('Twitter status updated.\n');
               console.log(response2+'\n');
           }
           });

Here is what I belive im supose to be doing but I don't know how to do that in the Node.js Oauth module. Posting image to twitter using Twitter+OAuth

Problem courtesy of: Adam Magaluk

Solution

Reviewing the code, it looks like there's no multipart/form-data handling at all in the node-oauth package right now. You can still use the node-oauth function to create the authorization header, but you'll have to do the multipart stuff on your own.

There are probably third-party libraries that can help out with that, but here's how I got it to work constructed by hand.

var data = fs.readFileSync(fileName);
var oauth = new OAuth(
    'https://api.twitter.com/oauth/request_token',
    'https://api.twitter.com/oauth/access_token',
    twitterKey, twitterSecret,
    '1.0', null, 'HMAC-SHA1');

var crlf = "\r\n";
var boundary = '---------------------------10102754414578508781458777923';

var separator = '--' + boundary;
var footer = crlf + separator + '--' + crlf;
var fileHeader = 'Content-Disposition: file; name="media"; filename="' + photoName + '"';

var contents = separator + crlf
    + 'Content-Disposition: form-data; name="status"' + crlf
    + crlf
    + tweet + crlf
    + separator + crlf
    + fileHeader + crlf
    + 'Content-Type: image/jpeg' +  crlf
    + crlf;

var multipartBody = Buffer.concat([
    new Buffer(contents),
    data,
    new Buffer(footer)]);

var hostname = 'upload.twitter.com';
var authorization = oauth.authHeader(
    'https://upload.twitter.com/1/statuses/update_with_media.json',
    accessToken, tokenSecret, 'POST');

var headers = {
    'Authorization': authorization,
    'Content-Type': 'multipart/form-data; boundary=' + boundary,
    'Host': hostname,
    'Content-Length': multipartBody.length,
    'Connection': 'Keep-Alive'
};

var options = {
    host: hostname,
    port: 443,
    path: '/1/statuses/update_with_media.json',
    method: 'POST',
    headers: headers
};

var request = https.request(options);     
request.write(multipartBody);
request.end();

request.on('error', function (err) {
    console.log('Error: Something is wrong.\n'+JSON.stringify(err)+'\n');
});

request.on('response', function (response) {            
    response.setEncoding('utf8');            
    response.on('data', function (chunk) {
        console.log(chunk.toString());
    });
    response.on('end', function () {
        console.log(response.statusCode +'\n');
    });
});    
Solution courtesy of: Dax Fohl

Discussion

There is currently no discussion for this recipe.

This recipe can be found in it's original form on Stack Over Flow.