Как отправить изображение на твиттер в Phonegap с помощью javascript
В настоящее время я использую приложение для веб-смартфонов с Phonegap. В этом приложении пользователи могут отправлять изображения, которые они снимают с помощью камеры телефона на Facebook. Эта функция была успешно реализована только с использованием javascript, отправив исходное 64-кодированное изображение. Теперь я хочу реализовать ту же функцию с помощью Twitter.
Я нашел несколько очень интересных сообщений в блоге об этом, и я уже могу обновить статус пользователя только с помощью javascript... но я тоже не могу отправлять изображения с помощью веб-службы update_with_media Twitter.
В соответствии с этим post кто-то говорит, что невозможно реализовать эту операцию без использования кода на стороне сервера (например, PHP скрипт).
Итак, мой вопрос: можно ли использовать веб-службу update_with_media Twitter только с помощью javascript?
Я посылаю вам свой код, чтобы получить обзор текущего решения. Я взял эту статью в качестве рабочей базы: http://oodlestechnologies.com/blogs/Twitter-integration-on-PhoneGap-using-ChildBrowser-and-OAuth-for-iOS-and-Android-Platforms
Вот мой HTML-код.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<script type="text/javascript" src="../js/jquery/jquery.min.js"></script>
<script type="text/javascript" src="../cordova-2.5.0.js"></script>
<script type="text/javascript" src="../js/childBrowser/childbrowser.js"></script>
<script type="text/javascript" src="../js/helpers/jsOAuth-1.3.6.js"></script>
<script type="text/javascript" src="../js/helpers/twitter.js"></script>
</head>
<body>
<h4>Oodles Twitter App</h4>
<table border="1">
<tr>
<th>Login using Twitter</th>
<th>
<button id="loginBtn" onclick="Twitter.init();">Login</button>
<button id="logoutBtn" onclick="logOut();">Logout</button>
</th>
</tr>
<tr id="tweetText">
<td colspan="2"><textarea id="tweet"></textarea></td>
</tr>
<tr id="tweetBtn">
<td colspan="2" align="right">
<button id="tweeter" onclick="Twitter.tweet();">Tweet</button>
</td>
</tr>
<tr><td colspan="2"><div id="welcome">Please Login to use this app</div></td></tr>
</table>
<br/>
<br/>
<button onclick="javascript:location.reload();">Recharger la page</button>
</body>
</html>
Вот мой код twitter.js: (Точка в методе post)
$(document).ready(function() {
document.addEventListener("deviceready", onDeviceReady, false);
});
function onDeviceReady() {
var root = this;
cb = window.plugins.childBrowser;
if (!localStorage.getItem(twitterKey)) {
$("#loginBtn").show();
$("#logoutBtn").hide();
$("tweetBtn").hide();
$("tweetText").hide();
}
else {
$("#loginBtn").hide();
$("#logoutBtn").show();
$("tweetBtn").show();
$("tweetText").show();
}
if (cb != null) {
cb.onLocationChange = function(loc) {
root.locChanged(loc);
};
cb.onClose = function() {
root.onCloseBrowser()
};
cb.onOpenExternal = function() {
root.onOpenExternal();
};
}
}
function onCloseBrowser() {
console.log("onCloseBrowser!");
}
function locChanged(loc) {
console.log("locChanged!");
}
function onOpenExternal() {
console.log("onOpenExternal!");
}
// Consumer key : ...
// Consumer secret : ...
// GLOBAL VARS
var oauth; // It Holds the oAuth data request
var requestParams; // Specific param related to request
var options = {consumerKey: '...', consumerSecret: '...', callbackUrl: "http://www.google.fr"};
var twitterKey = "twtrKey"; // This key is used for storing Information related
var Twitter = {
init: function() {
// Apps storedAccessData , Apps Data in Raw format
var storedAccessData, rawData = localStorage.getItem(twitterKey);
// here we are going to check whether the data about user is already with us.
if (localStorage.getItem(twitterKey) !== null) {
// when App already knows data
storedAccessData = JSON.parse(rawData); //JSON parsing
//options.accessTokenKey = storedAccessData.accessTokenKey; // data will be saved when user first time signin
options.accessTokenSecret = storedAccessData.accessTokenSecret; // data will be saved when user first first signin
// javascript OAuth take care of everything for app we need to provide just the options
oauth = OAuth(options);
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
console.log("USERNAME: " + entry.screen_name);
}
);
}
else {
// we have no data for save user
oauth = OAuth(options);
oauth.get('https://api.twitter.com/oauth/request_token',
function(data) {
requestParams = data.text;
cb.showWebPage('https://api.twitter.com/oauth/authorize?' + data.text); // This opens the Twitter authorization / sign in page
cb.onLocationChange = function(loc) {
Twitter.success(loc);
}; // Here will will track the change in URL of ChildBrowser
},
function(data) {
console.log("ERROR: " + JSON.stringify(data));
}
);
}
},
/*
When ChildBrowser URL changes we will track it here.
We will also be acknowledged was the request is a successful or unsuccessful
*/
success: function(loc) {
// Here the URL of supplied callback will Load
/*
Here Plugin will check whether the callback Url matches with the given Url
*/
if (loc.indexOf("http://www.google.fr") >= 0) {
// Parse the returned URL
var index, verifier = '';
var params = loc.substr(loc.indexOf('?') + 1);
params = params.split('&');
for (var i = 0; i < params.length; i++) {
var y = params[i].split('=');
if (y[0] === 'oauth_verifier') {
verifier = y[1];
}
}
// Here we are going to change token for request with token for access
/*
Once user has authorised us then we have to change the token for request with token of access
here we will give data to localStorage.
*/
oauth.get('https://api.twitter.com/oauth/access_token?oauth_verifier=' + verifier + '&' + requestParams,
function(data) {
var accessParams = {};
var qvars_tmp = data.text.split('&');
for (var i = 0; i < qvars_tmp.length; i++) {
var y = qvars_tmp[i].split('=');
accessParams[y[0]] = decodeURIComponent(y[1]);
}
$('#oauthStatus').html('<span style="color:green;">Success!</span>');
$('#stage-auth').hide();
$('#stage-data').show();
oauth.setAccessToken([accessParams.oauth_token, accessParams.oauth_token_secret]);
// Saving token of access in Local_Storage
var accessData = {};
accessData.accessTokenKey = accessParams.oauth_token;
accessData.accessTokenSecret = accessParams.oauth_token_secret;
// Configuring Apps LOCAL_STORAGE
console.log("TWITTER: Storing token key/secret in localStorage");
localStorage.setItem(twitterKey, JSON.stringify(accessData));
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
console.log("TWITTER USER: " + entry.screen_name);
$("#welcome").show();
document.getElementById("welcome").innerHTML = "welcome " + entry.screen_name;
successfulLogin();
// Just for eg.
app.init();
},
function(data) {
console.log("ERROR: " + data);
}
);
// Now we have to close the child browser because everthing goes on track.
window.plugins.childBrowser.close();
},
function(data) {
console.log(data);
}
);
}
else {
// Just Empty
}
},
tweet: function() {
var storedAccessData, rawData = localStorage.getItem(twitterKey);
storedAccessData = JSON.parse(rawData); // Paring Json
options.accessTokenKey = storedAccessData.accessTokenKey; // it will be saved on first signin
options.accessTokenSecret = storedAccessData.accessTokenSecret; // it will be save on first login
// javascript OAuth will care of else for app we need to send only the options
oauth = OAuth(options);
oauth.get('https://api.twitter.com/1/account/verify_credentials.json?skip_status=true',
function(data) {
var entry = JSON.parse(data.text);
Twitter.post();
}
);
},
/*
We now have the data to tweet
*/
post: function() {
alert('Post !');
var theTweet = $("#tweet").val(); // You can change it with what else you likes.
oauth.post('https://upload.twitter.com/1/statuses/update_with_media.json',
{
'status': theTweet,
'media': //HERE IS THE PROBLEM, WHAT TO DO HERE ?
}, "multipart/form-data",
function(data)
{
alert('Data 1 !');
console.log('------Data1 : ' + data);
var entry = JSON.parse(data.text);
console.log(entry);
done();
},
function(data) {
//var json_result = JSON.parse(data);
//alert(json_result.text.error);
var entry = JSON.stringify(data);
console.log('------Data2 : ' + entry);
}
);
}
}
function done() {
alert("OKKK !");
$("#tweet").val('');
}
function successfulLogin() {
$("#loginBtn").hide();
$("#logoutBtn,#tweet,#tweeter,#tweetBtn,#tweetText").show();
}
function logOut() {
//localStorage.clear();
window.localStorage.removeItem(twitterKey);
document.getElementById("welcome").innerHTML = "Please Login to use this app";
$("#loginBtn").show();
$("#logoutBtn,#tweet,#tweeter,#tweetText,#tweetBtn").hide();
}
После многих тестов (отправка изображения base64, отправка блоба, отправка двоичного файла,...) вот сообщение возврата из Twitter. У меня есть:
{\ "ошибки \": [{\ "сообщение \":\ "Внутренние ошибка \" \ "код \": 131}]} "," XML ":" "," requestHeaders ": {" Content-Type ":" многочастному/форм-данных "}," responseHeaders ": {" Дата ":" Пт, 19 апр 2013 15:45:28 GMT "" Content-Encoding ":" сбрасываться "," строгая-транспортной-безопасность ":" Макс-возраст = 631138519 "," Статус ":" 500 Внутренний сервер Ошибка "" сервер ":" TFE "," тип содержимого ":" приложения /JSON; кодировка = UTF-8 "," вариант ":" HTTP/1.1"}}
"Форум" (посылая blob) был опубликован на форуме Twitter dev, но не работает для меня: dev.twitter.com/discussions/6969
Кто-нибудь хочет реализовать ту же функцию или иметь решение? Спасибо!
------ EDITED:
Я просто хочу использовать Javascript, и я не хочу реализовывать какое-либо серверное решение (без PHP, С#, Java...).
Ответы
Ответ 1
Согласно документам, Twitter требует multipart/form-data
enctype, что означает, что строка base64 не будет работать.
В отличие от статусов POST/обновления, этот метод ожидает необработанных многостраничных данных. Ваш запрос POST Content-Type должен быть установлен в multipart/form-data с параметром media [] ~ https://dev.twitter.com/docs/api/1/post/statuses/update_with_media
Однако вы можете разместить конечную точку, которая принимает base64, преобразует ее в реальный файл и перенаправляет запрос в Twitter. Например (непроверенный):
<?php
$base64 = $_POST['image'];
$data = base64_decode( $base64 );
// Make name unique to avoid conflicts.
$temp_file = uniqid() . $_POST['name'];
// Save the file to a temp location.
file_put_contents( $temp_file, $data );
$temp_info = pathinfo( $temp_file );
$temp_type = $temp_info['extension'];
$temp_name = basename( $temp_file, '.' . $temp_type );
// OAuth library recommended by Twitter: https://github.com/themattharris/tmhOAuth
// See original: https://github.com/themattharris/tmhOAuth-examples/blob/master/images.php
require 'tmhOAuth.php';
require 'tmhUtilities.php';
$tmhOAuth = new tmhOAuth( array(
'consumer_key' => $_POST['consumer_key'],
'consumer_secret' => $_POST['consumer_secret'],
'user_token' => $_POST['user_token'],
'user_secret' => $_POST['user_secret'],
));
// note the type and filename are set here as well
// Edit: Not sure if the `type` and `filename` params are necessary.
$params = array( 'media[]' => "@{$temp_file};type={$temp_type};filename={$temp_name}" );
$code = $tmhOAuth->request( 'POST', $tmhOAuth->url( '1/status/update_with_media' ),
$params,
true, // use auth
true // multipart
);
// Remove temp file.
unlink( $temp_file );
if ( $code == 200 ) {
tmhUtilities::pr( json_decode( $tmhOAuth->response['response'] ) );
}
tmhUtilities::pr( htmlentities( $tmhOAuth->response['response'] ) );
?>
И вы можете называть его так:
$.ajax({
// You'll want to use https to protect the oauth info.
url: "https://mysite.com/proxy.php",
type: "POST",
data: {
image: "base64 data...",
name: "foo.png",
consumer_key: options.consumerKey,
consumer_secret: options.consumerSecret,
user_token: options.accessTokenKey,
user_secret: options.accessTokenSecret
},
success: function( data ) {
console.log( data );
}
});
Ответ 2
Для тех, кто пытается размещать изображения в Twitter с помощью клиента JS, я смог опубликовать в twitter, используя решение gary-buynary-co-za (https://github.com/bytespider/jsOAuth/pull/11) в конце этого форума. В значительной степени это привело к использованию объектов Phonegap FileTransfer и FileTransferOptions для передачи изображения в twitter api, но использовалось jsOAuth для подготовки заголовков и подписей FileTransferOptions. Решение, безусловно, можно было бы очистить.