bytespider / jsOAuth

JavaScript implimentation of the OAuth protocol. Currently supports version 1.0 (RFC5849) of the specification. Node.js & CommonJS compatible.
http://bytespider.github.com/jsOAuth/
MIT License
556 stars 108 forks source link

Form data posting #11

Closed lukaszkorecki closed 13 years ago

lukaszkorecki commented 13 years ago

This patch adds the ability to post files via OAuth, unfortunately it requires FormData, but from what I can see it's available in pretty much any modern browser, namely those which can be used for building rich apps.

It's completely transparent, so that the old code works as well, i.e:

Given that oauth is an working OAuth object and can make requests this will work like the old code

oath.post('some.url.com', { status : "hi!"}, callbacks.onSuccess, callbacks.onFailure);

Now this will change the internal behaviour:

var file = document.querySelector('input[type="file"]').files[0];
oauth.post('some.url.com/file_upload', { status : 'hi', attachment : file, } callbacks.onSuccess, callbacks.onFailure);

and post all data without signing it.

My initial version had two methods :post (the old one) and postWithFile but I figured that it doesn't make sense to create more branches in the code.

I've tested this against Blip.pl API, I'm pretty sure it works with Twitpic, but I haven't tried that yet.

RSR commented 12 years ago

Hi i have an image and i want to send it as an attachment . I don't know how to pass a file object of that image , all i have is the relative URL to that image. So please help me on this. I would be very grateful hearing back from you...

lukaszkorecki commented 12 years ago

Hi

So there are two major limitations to this functionality:

browser/engine

I need to know where are you using jsOAuth - is it a desktop browser, web runtime like Titanium or is it for mobile (PhoneGap etc).

File posting in general works only in web runtimes and desktop browsers. So you can't make it work in lets say webOS or iPhone because <input type="file" /> doesn't work there at all. More information about FormData and where can I use XHR2.

In theory it should be possible to get the raw data of the file, make it into content-type: multipart and just POST it. Which leads me to the second part:

API you're working with

My code was made to work with APIs which accept direct file uploads via POST/multipart (like (blip.pl)[http://blip.pl]) - if you want to use TwitPic's OAuth Echo it's much more involved and as far as I know it hasn't been done (yet).

It might work with Twitter's Pic upload, but I haven't tried it - probably worth looking into it, since it's going to be most popular use case.

RSR commented 12 years ago

I am using jsOAuth for mobile apps(PhoneGap). As you said the 'input type file ' doesnt work at all, So what you are saying is that i can get the raw data of the file(image) and POST it ... ?

lukaszkorecki commented 12 years ago

You should be able to use PhoneGap's File object and do some magic with turning the raw data into content-type/multipart and then POST it like any other string

naokazuterada commented 12 years ago

Hi, lukaszkorecki

Thanks for your nice library !! I can tweet perfectly by using this with phonegap.

But I'm also couldnot upload image (with twitter api 'update_with_media' )... You mentioned about it, but I cannot understand, sorry... Could you tell me procedures more ?

thanks

lukaszkorecki commented 12 years ago

Nah, @bytespider gets the credit for jsOauth, I just help here and there.

As for uploads - you'd have to do one of the things described in this blog post.

I didn't have time to try out file upload with Phonegap unfortunately.

naokazuterada commented 12 years ago

@lukaszkorecki

OK, i see. I keep try it. Thanks quick replay !

gerbz commented 12 years ago

Same issue here using jsOauth in PhoneGap to post an image to tumblr via their api. Tumblr requires "url-encoded binary content" for the image. PhoneGap only offers a filepath OR a base64 encoded string of the image.

When trying to post with jsOauth using the filepath it just passes the filepath along as a string like: _file:///var/folders/qy/2jvsbc9n4bd9d19xs0hvnhn00000gp/T/cdv_photo048.jpg

Any idea why? How can I get from filepath to url-encoded binary data? cc: @lukaszkorecki @bytespider

lukaszkorecki commented 12 years ago

Does PhoneGap's File API work exactly like browser's File object??

Looks like FileReader could be used for reading the file as plain text (readAsText method) and then using encodeURI to make it 'url-encoded'. Last step is simply POST ing url-encoded file data just like any with any other POST request (not a file upload, that will not work in PhoneGap because DOM File API is not available in most mobile web browsers except iOS6 or whatever the latest version was announced at WWDC)

gerbz commented 12 years ago

FileReader can not get binary data. Only string data is passed between the native and javascript layers in phonegap. https://groups.google.com/d/msg/phonegap/ksBmb-oNShM/aUwFyGE1Y0wJ

So I have 2 options (neither of which im having any luck): 1) request base64 image data from PhoneGap, decode it and pass it to jsOauth 2) request the FILE_URI from PhoneGap and figure out how to convince jsOauth to handle it

It sounds like jsOauth expects the filepath to be available via form data and thats why option 2 does not work.

bytespider commented 12 years ago

I have a version that contains a fix that should detect the presence of a PhoneGap File object in the data object. Say if you were grabbing a image from the camera you could do something like this:

You'll need to ask the camera for the image as a file url http://docs.phonegap.com/en/2.0.0/cordova_camera_camera.md.html#camera.getPicture

Which you'll then need to use http://docs.phonegap.com/en/2.0.0/cordova_file_file.md.html#LocalFileSystem resolveLocalFileSystemURI:

which gets you a fileEntry which you can then use http://docs.phonegap.com/en/2.0.0/cordova_file_file.md.html#FileEntry file() to get the file.

Then you can pass it into jsOAuth like this

pictureTakenWithCamera = fileEntry.file();
oauth.post('https://upload.twitter.com/1/statuses/update_with_media.json', {media:[pictureTakenWithCamera]}, success, failure);

Give it a go an let me know how it works out

gwest7 commented 11 years ago

Hi bytespider

I tried did as you described above, but some of the older phones' browser (Android 2.3) does not support FormData. Any advice?

Thank you for the help.

gwest7 commented 11 years ago

Tried using https://github.com/mattfawcett/form-data-compatibility, but now get an Internal Server Error response from Twitter.

bytespider commented 11 years ago

Unfortunately I can't help here. We tested it with the W3C API and even that was a hack.
Sorry.

Rob Griffiths Sent with Sparrow (http://www.sparrowmailapp.com/?sig)

On Monday, 7 January 2013 at 11:03, gary-buynary-co-za wrote:

Tried using https://github.com/mattfawcett/form-data-compatibility, but now get an Internal Server Error response from Twitter.

— Reply to this email directly or view it on GitHub (https://github.com/bytespider/jsOAuth/pull/11#issuecomment-11947872).

gwest7 commented 11 years ago

Thank you for the reply.

I managed to upload a photo using PhoneGap's FileTransfer.upload. I duplicated the jsOAuth.request method and set the headers on the the FileTransfer object instead of the ajax request object. Tested it on iOS 5 and Android 2.3, but BlackBerry still gives me a http status of 401.

This then makes our copy of jsOAuth dependent on PhoneGap.

gwest7 commented 11 years ago

And now we managed to get BlackBerry OS 6 working as well. Phonegap versions 2.2.0 had a bug where custom headers were not being set. This is fixed with PhoneGap 2.3.0.

blamot commented 11 years ago

Gary, do you have this code online somewhere? Banging my head against the wall to get this to work...

gwest7 commented 11 years ago

Hi Blamot.

Have a look at http://plnkr.co/edit/m2MUSyIQ3EnJwQYiwIC6?p=preview

There is a modified js-OAuth 'file'. We added the push method which calls the our jsOAuth upload method.

push("https://api.twitter.com/1.1/statuses/update_with_media.json",
    imageURI.substr(imageURI.lastIndexOf("/") + 1),
    new FileUploadOptions("media", "image.jpg", "image/jpeg", {status:tweetStatus}, {}),
    onTweetSuccess,
    onTweetError
);

Hope you have success.

csenn commented 10 years ago

Thanks a ton gary-buynary-co-za, I finally got Twitter posts working using your work around!

Only thing that was different was I used just imageURI instead of imageURI.substr(imageURI.lastIndexOf("/") + 1) as second parameter.

Still not sure why posting PhoneGap's fileEntry.file(success,fail) success result (as shown in bytespider's explanation) did not end up working, maybe something with the image parameters...

waqasghouri89 commented 10 years ago

What is imageURI at here? is their any way to upload with base64

gwest7 commented 10 years ago

Hi waqasghouri89

imageURI is the path to the file stored locally on the device.

Twitter only accepts multi part form posts to update_with_media.

waqasghouri89 commented 10 years ago

I'm trying to post image using cordova plugin on twitter but no success. Before above comment by "gary-buynary-co-za" I was trying to upload base64 to twitter media using this code.

oauth.post('https://api.twitter.com/1.1/statuses/update_with_media.json', { 'status' : theTweet, // javascript OAuth encodes this 'media[]': base64Data }, { 'Content-Type':"multipart/form-data" }, function(data) { console.log('uploaded success'); console.log(JSON.stringify(data));
}, function(data) { console.log('uploading failed try agian'); console.log(JSON.stringify(data)); } );

but now I tried to post image with file URI using this code. I'm saving base64 to png in file system by following this reference.

"http://stackoverflow.com/questions/11388018/phonegap-plugin-to-convert-base64-string-to-a-png-image-in-android"

oauth.post('https://api.twitter.com/1.1/statuses/update_with_media.json', { 'status' : theTweet, // javascript OAuth encodes this 'media[]': ['file:///storage/sdcard0/Pictures/intraining.png'] }, { 'Content-Type':"multipart/form-data" }, function(data) { console.log('uploaded success'); console.log(JSON.stringify(data));
}, function(data) { console.log('uploading failed try agian'); console.log(JSON.stringify(data)); } );

But Always get "MISSING OR INVALID URL PARAMETER" response from twitter .

Plz help me out. I'm using phonegap plugin cordova 2.4.0 on android and nothing found any solution. plz help

gwest7 commented 10 years ago

Hi waqasghouri89

File upload (multipart/form-data) is not as simple as that. The post field for media needs to be in a very specific format.

You don't have to worry about the format however as PhoneGap has an upload method in FileTransfer. See http://docs.phonegap.com/en/1.0.0/phonegap_file_file.md.html#FileTransfer

There is one snag to using FileTransfer though and that is that it does not follow the oAuth protocol. It leave that for you to add.

You add it by tweaking the excellent tool that jsOAuth is and make it add the oAuth headers to FileTransfer instead of the normal post.

This tweak is ready for you to use in http://plnkr.co/edit/m2MUSyIQ3EnJwQYiwIC6?p=preview

Just look for the push method as described above. Worked with 2.3, should work for 2.4.

waqasghouri89 commented 10 years ago

Thanks gary-buynary-co-za, The method you share on above comment couldn't help me. I already checked it out. But i solved my problem with different way and i want to share for others.

I create a new phone gap plugin which call native function and provide it share url address, accessToken, accessSecret and status. and navative function upload image using stream and upload it on provided accessToken and accessSecret.

Check this link, here i post my question and provide a solution for phonegap cordova with android integration only.

http://stackoverflow.com/questions/21779605/how-to-send-in-media-parameters-for-update-with-media-api-call-of-twitter-usin/21822640#21822640

gwest7 commented 10 years ago

That looks good! Especially the consumer key and secret being kept in the Java code. Are you also exploring an iOS solution?

Just as a bit of closure could you briefly explain why the FileTransfer + jsOAuth method I use did not work for you? I am very interested to know.

waqasghouri89 commented 10 years ago

Thanks for appreciating, I'm exploring on iOS ASAP and after done i will share the code, because its a part of my current project :-) so i have to do this in any case...

Actually when i tried to implement FileTransfer + jsOAuth and use that code

  push("https://api.twitter.com/1.1/statuses/update_with_media.json",
        imageURI.substr(imageURI.lastIndexOf("/") + 1),
        new FileUploadOptions("media", "image.jpg", "image/jpeg", {status:tweetStatus}, {}),
       onTweetSuccess,
       onTweetError

);

its prompt me error that FileTransfer.java class not found but when i create a new project and use cordova default example
http://docs.phonegap.com/en/2.0.0/cordova_file_file.md.html its work, i could not exactly figured out the probelm. And second thing i don't want to save my file on mobile and then grab its link from mobile and then posting on twitter with multipart is quite too long process. so using URL address its fast and easy implementation....