Założenie, że przy przesyłaniu zdjęć ma być Tumblr API, będzie łatwe. Nie jest. (EDIT jest teraz, patrz Edycja 2 na końcu tego wpisu)Przesyłanie obrazów do interfejsu API tumblr z systemu Android
Moja aplikacja ma przesłać obraz do tumblr
. Wolałbym to zrobić z usługi, ale na razie korzystam z działania, które zamyka się zaraz po zakończeniu przesyłania. W OnCreate()
uwierzytelnieniu użytkownika:
consumer = new CommonsHttpOAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET);
// It uses this signature by default
// consumer.setMessageSigner(new HmacSha1MessageSigner());
provider = new CommonsHttpOAuthProvider(REQUEST_TOKEN_URL,ACCESS_TOKEN_URL,AUTH_URL);
String authUrl;
try
{
authUrl = provider.retrieveRequestToken(consumer, CALLBACK_URL);
Log.d(TAG, "Auth url:" + authUrl);
startActivity(new Intent("android.intent.action.VIEW", Uri.parse(authUrl)));
}
To otwiera działalność przeglądarki, w którym użytkownik może dodać nazwę użytkownika i passoword a następnie powraca aplikacji do działania (jest to również, dlaczego muszę używać aktywność, I don” t wiedzieć, jak to zrobić z usługi)
Wracając z przeglądarki dane są wyodrębnione:
Uri uri = context.getIntent().getData();
if (uri != null && uri.toString().startsWith(CALLBACK_URL))
{
Log.d(TAG, "uri!=null");
String verifier = uri.getQueryParameter("oauth_verifier");
Log.d(TAG, "verifier"+verifier);
try
{
provider.setOAuth10a(true);
provider.retrieveAccessToken(consumer, verifier);
Log.d(TAG, "try");
}
catch (Exception e)
{
Log.e(TAG, e.toString());
e.printStackTrace();
}
OAUTH_TOKEN = consumer.getToken();
OAUTH_SECRET = consumer.getTokenSecret();
Większość z tych dwóch fragmentów dostałem from here i działają dobrze.
Dzięki tym tokenom mogę teraz próbować umieszczać dane na tumblr. Gdy próbuję dodać tekst to działa prawidłowo przy użyciu tej metody:
private void createText()
{
if(!OAUTH_TOKEN.equals(""))
{
HttpContext context = new BasicHttpContext();
HttpPost request = new HttpPost("http://api.tumblr.com/v2/blog/" + blogname + ".tumblr.com/post");
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
nameValuePairs.add(new BasicNameValuePair("type", "text"));
nameValuePairs.add(new BasicNameValuePair("body", "this is just a test"));
try
{
request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
}
catch (UnsupportedEncodingException e1)
{
Log.e(TAG, e1.toString());
e1.printStackTrace();
}
if (consumer == null)
{
consumer = new CommonsHttpOAuthConsumer(OAuthConstants.TUMBR_CONSUMERKEY, OAuthConstants.TUMBR_SECRETKEY);
}
if (OAUTH_TOKEN == null || OAUTH_SECRET == null)
{
Log.e(TAG, "Not logged in error");
}
consumer.setTokenWithSecret(OAUTH_TOKEN, OAUTH_SECRET);
try
{
consumer.sign(request);
}
catch (OAuthMessageSignerException e)
{
}
catch (OAuthExpectationFailedException e)
{
}
catch (OAuthCommunicationException e)
{
}
HttpClient client = new DefaultHttpClient();
//finally execute this request
try
{
HttpResponse response = client.execute(request, context);
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null)
{
Log.d(TAG, "responseEntety!=null");
try
{
Log.d(TAG, EntityUtils.toString(responseEntity));
}
catch (ParseException e)
{
e.printStackTrace();
Log.e(TAG, e.toString());
}
catch (IOException e)
{
e.printStackTrace();
Log.e(TAG, e.toString());
} // gives me {"meta":{"status":401,"msg":"Not Authorized"},"response":[]} when I try to upload a photo
}
else
{
Log.d(TAG, "responseEntety==null");
}
}
catch (ClientProtocolException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
PostToTumblr.this.finish();
}
Jak widać tutaj http://www.tumblr.com/blog/snapnowandroid (przynajmniej w tej chwili) tekst „to tylko test” jest zamieszczona.
Jednak, gdy próbuję publikować obrazy, robi się dziwnie. Teraz sprawdziłem i najwyraźniej jest to znany problem z API tumblr, które zostało nadmiernie omówione here, a niektóre rozwiązały je w innych językach programowania (na przykład here), ale nie udało mi się powtórzyć tych sukcesów.
Metoda (w całości poniżej) ma dokładnie taką samą strukturę powyższej metody (które działa), to nameValuePairs to tylko inna
otrzymuje zmienną bitmapy nazwie zdjęcie Metoda:
private void uploadToTumblr(Bitmap photo)
Ta mapa bitowa jest przekształcany w tablicy:
ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();
w nameValuePairs są napełniane następująco:
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("type", enc), URLEncoder.encode("photo", enc)));
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("caption", enc), URLEncoder.encode(text, enc)));
nameValuePairs.add(new BasicNameValuePair("data", Base64.encodeToString(bytes, Base64.URL_SAFE)));
Wynikiem jest {"meta":{"status":400,"msg":"Bad Request"},"response":{"errors":["Error uploading photo."]}}
z api interfejsu tumblr.
Próbuję kodowania obrazu inaczej, jak opisano w this article, ale bez żadnych zmian.
//http://www.coderanch.com/t/526487/java/java/Java-Byte-Hex-String
final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
char[] hexChars = new char[bytes.length * 3];
int v;
for (int j = 0; j < bytes.length; j++)
{
v = bytes[j] & 0xFF;
hexChars[j * 3] = '%';
hexChars[j * 3 + 1] = hexArray[v >>> 4];
hexChars[j * 3 + 2] = hexArray[v & 0x0F];
}
String s = new String(hexChars);
s = URLEncoder.encode(s, enc);
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("data", enc), s));
Oto cała metoda (bez kodowania hex):
private void uploadToTumblr(Bitmap photo)
{
if(!OAUTH_TOKEN.equals(""))
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();
String text ="SNAP";
HttpContext context = new BasicHttpContext();
HttpPost request = new HttpPost("http://api.tumblr.com/v2/blog/" + blogname + ".tumblr.com/post");
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
String enc = "UTF-8";
try
{
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("type", enc), URLEncoder.encode("photo", enc)));
nameValuePairs.add(new BasicNameValuePair(URLEncoder.encode("caption", enc), URLEncoder.encode(text, enc)));
nameValuePairs.add(new BasicNameValuePair("data", Base64.encodeToString(bytes, Base64.URL_SAFE)));
}
catch (UnsupportedEncodingException e2)
{
Log.e(TAG, e2.toString());
e2.printStackTrace();
}
try
{
request.setEntity(new UrlEncodedFormEntity(nameValuePairs));
}
catch (UnsupportedEncodingException e1)
{
Log.e(TAG, e1.toString());
e1.printStackTrace();
}
if (consumer == null)
{
consumer = new CommonsHttpOAuthConsumer(OAuthConstants.TUMBR_CONSUMERKEY, OAuthConstants.TUMBR_SECRETKEY);
}
if (OAUTH_TOKEN == null || OAUTH_SECRET == null)
{
//throw new LoginErrorException(LoginErrorException.NOT_LOGGED_IN);
Log.e(TAG, "Not logged in error");
}
consumer.setTokenWithSecret(OAUTH_TOKEN, OAUTH_SECRET);
try
{
consumer.sign(request);
}
catch (OAuthMessageSignerException e)
{
}
catch (OAuthExpectationFailedException e)
{
}
catch (OAuthCommunicationException e)
{
}
HttpClient client = new DefaultHttpClient();
//finally execute this request
try
{
HttpResponse response = client.execute(request, context);
HttpEntity responseEntity = response.getEntity();
if (responseEntity != null)
{
Log.d(TAG, "responseEntety!=null");
try
{
Log.d(TAG, EntityUtils.toString(responseEntity));
}
catch (ParseException e)
{
e.printStackTrace();
Log.e(TAG, e.toString());
}
catch (IOException e)
{
e.printStackTrace();
Log.e(TAG, e.toString());
}
}
else
{
Log.d(TAG, "responseEntety==null");
}
}
catch (ClientProtocolException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else
{
Log.d(TAG, "upload imposble... Toklen not set");
}
PostToTumblr.this.finish();
}
Teraz, gdy istnieje kilka rzeczy, nie jestem zadowolony z (na przykład, że odbywa się to za pomocą aktywności zamiast usługi) istotną kwestią jest oczywiście problem przesyłania zdjęć. Nie jestem pierwszym, który ma ten problem, więc czy ktoś był w stanie to zrobić w Javie?
Edycja 1
nie dokonano żadnego postępu z problemem pod ręką, ale stworzył obejście, które mogą być miły dla ludzi, którzy mają ten sam problem. Tumblr oferuje posting via mail i można zaprogramować Androida do wysyłania wiadomości e-mail w tle jako shown here. Działa to bardzo dobrze, ale musisz poprosić użytkowników o podanie danych swojego konta pocztowego i adresu Tumblr-mail, aby opublikować.
Edycja 2
Lata pased i za pomocą poczty elektronicznej nie jest łatwy sposób to zrobić. Z jumblr jest wreszcie dobre API dla Javy, które będzie działało na Androidzie. Uwierzytelnianie OAuth nie jest zabawne (nigdy nie jest), ale kiedy już to minie, jest fantastyczne.
Teraz technicznie pytanie o to, jak wykonać uwierzytelnienie, nie należy tutaj, ale jest to moje zbyt długie pytanie, więc wkleję tutaj kod i jeśli nie jest to interesujące, po prostu pomiń to.
ta wykorzystuje słoik nazwie jumblr-0.0.10-jar-z-dependencies.jar
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.util.Log;
import com.tumblr.jumblr.JumblrClient;
import com.tumblr.jumblr.request.RequestBuilder;
import com.tumblr.jumblr.types.Blog;
import com.tumblr.jumblr.types.User;
import org.scribe.builder.ServiceBuilder;
import org.scribe.builder.api.TumblrApi;
import org.scribe.model.Token;
import org.scribe.model.Verifier;
import org.scribe.oauth.OAuthService;
import java.io.File;
public class Tumblr
{
private static final String PROTECTED_RESOURCE_URL = "http://api.tumblr.com/v2/user/info";
static OAuthService service;
static Token requestToken=null;
public static void share(final Activity ctx, File file)
{
Thread tt = new Thread(new Runnable()
{
@Override
public void run()
{
JumblrClient client = new JumblrClient(Tumblr_Constants.CONSUMER_KEY, Tumblr_Constants.CONSUMER_SECRET);
RequestBuilder requestBuilder = client.getRequestBuilder();
requestBuilder.setConsumer(Tumblr_Constants.CONSUMER_KEY, Tumblr_Constants.CONSUMER_SECRET);
SharedPreferences settings = ctx.getSharedPreferences("TumblrData", 0);
String oauthToken=settings.getString("OauthToken", "");
String oauthTokenSecret=settings.getString("OauthSecret", "");
if(oauthToken.equals("") || oauthTokenSecret.equals(""))
{
authenticate(ctx);
while(WebViewFragment.verifier.equals(""))
{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
String v = WebViewFragment.verifier;
Token accessToken = authenticatefurther(v);
SharedPreferences.Editor edit = settings.edit();
edit.putString("OauthToken", accessToken.getToken());
edit.putString("OauthSecret", accessToken.getSecret());
edit.commit();
oauthToken=settings.getString("OauthToken", "");
oauthTokenSecret=settings.getString("OauthSecret", "");
}
if(!oauthToken.equals("") && !oauthTokenSecret.equals(""))
{
client.setToken(oauthToken, oauthTokenSecret);
User user = client.user();
System.out.println(user.getName());
for (Blog blog : user.getBlogs()) {
Log.d("TUMBLR", blog.getTitle());
}
}
}
});
tt.start();
}
private static void authenticate(Context ctx) {
service = new ServiceBuilder()
.provider(TumblrApi.class)
.apiKey(Tumblr_Constants.CONSUMER_KEY)
.apiSecret(Tumblr_Constants.CONSUMER_SECRET)
.callback("snapnao://snapnao.de/ok") // OOB forbidden. We need an url and the better is on the tumblr website !
.build();
Log.d("TUMBLR", "=== Tumblr's OAuth Workflow ===");
System.out.println();
// Obtain the Request Token
Log.d("TUMBLR", "Fetching the Request Token...");
requestToken = service.getRequestToken();
Log.d("TUMBLR", "Got the Request Token!");
Log.d("TUMBLR", "");
Log.d("TUMBLR", "Now go and authorize Scribe here:");
Log.d("TUMBLR", service.getAuthorizationUrl(requestToken));
String url = service.getAuthorizationUrl(requestToken);
Intent i = new Intent(ctx, WebViewFragment.class);
i.putExtra("url", url);
ctx.startActivity(i);
}
private static Token authenticatefurther(String v)
{
Token accessToken = null;
Log.d("TUMBLR", "And paste the verifier here");
Log.d("TUMBLR", ">>");
Verifier verifier = new Verifier(v);
Log.d("TUMBLR", "");
// Trade the Request Token and Verfier for the Access Token
Log.d("TUMBLR", "Trading the Request Token for an Access Token...");
accessToken = service.getAccessToken(requestToken ,
verifier);
Log.d("TUMBLR", "Got the Access Token!");
Log.d("TUMBLR", "(if your curious it looks like this: " + accessToken + ")");
Log.d("TUMBLR", "");
return accessToken;
}
}
WebViewFragement wygląda następująco:
import android.app.Activity;
import android.graphics.Bitmap;
import android.net.http.SslError;
import android.os.Bundle;
import android.util.Log;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class WebViewFragment extends Activity
{
public static String verifier="";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.webviewfragment);
String url = getIntent().getStringExtra("url");
Log.d("TUMBLR", "webview-> "+url);
WebView view = (WebView) findViewById(R.id.webView);
view.setWebViewClient(
new SSLTolerentWebViewClient()
);
view.getSettings().setJavaScriptEnabled(true);
view.loadUrl(url);
}
// SSL Error Tolerant Web View Client
private class SSLTolerentWebViewClient extends WebViewClient {
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed(); // Ignore SSL certificate errors
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d("TUMBLR", "+++++"+url);
if(url.contains("oauth_verifier="))
{
String[] x = url.split("oauth_verifier=");
verifier=x[1].replace("#_=_", "");
WebViewFragment.this.finish();
}
}
}
}
Dlaczego czy nie kodujesz nazwy wejściowej 'data' w parach nazwa-wartość, tak jak ty, to' type' i 'caption'? – Madbreaks
Faktycznie kodowanie nazwy wejściowej nie jest konieczne (jak widać z działającego kodu dla postów tekstowych na początku posta). To tylko jedna z rzeczy, które próbowałem uruchomić. Powinieneś to powtórzyć dla jasności w przykładowym kodzie. –
Pierwszą rzeczą, którą zauważam jest to, że używasz 'data', a nie' source', ale wysyłasz mu ciąg, a nie tablicę. Spróbuj użyć 'source' z zakodowanym łańcuchem lub' data' jako tablicą z jednym zakodowanym elementem ciągu, jeśli właśnie przesyłasz jedno zdjęcie. – Ally