从Android上传图像到tumblr API



假设使用Tumblr API上传图像会很容易。事实并非如此。(编辑现在,请参阅本条目末尾的编辑2)

我的应用程序应该将图像上传到tumblr。我更喜欢在服务中这样做,但目前我使用的活动在上传完成后立即关闭。在OnCreate()中,用户通过身份验证:

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)));
} 

这打开了一个浏览器活动,用户可以在其中添加用户名和密码,然后应用程序返回到该活动(这也是我必须使用活动的原因,我不知道如何从服务中做到这一点)

从浏览器返回提取数据:

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();

这两个片段大部分都是我从这里得到的,效果很好。

有了这些令牌,我现在可以尝试将数据放在tumblr上。当我尝试添加文本时,使用这种方法可以很好地工作:

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();
}

正如你在这里看到的http://www.tumblr.com/blog/snapnowandroid(至少到目前为止)发布了文本"这只是一个测试"。

然而,当我试图发布图片时,它会变得奇怪。现在我检查了一下,显然这是tumblr API的一个众所周知的问题,这里已经过多地讨论了这个问题,有些人已经用其他编程语言解决了这个问题(例如这里),但我无法重复这些成功。

该方法(以下全部)与上述方法(有效)具有完全相同的结构,名称ValuePairs只是不同的

该方法提供了一个名为photo:的位图变量

private void uploadToTumblr(Bitmap photo)

此位图被转换为数组:

ByteArrayOutputStream stream = new ByteArrayOutputStream();
photo.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] bytes = stream.toByteArray();

名称ValuePairs的填充方式如下:

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))); 

结果是来自tumblrAPI的{"meta":{"status":400,"msg":"Bad Request"},"response":{"errors":["Error uploading photo."]}}

我试着用不同的方式编码图片,如本文所述,但没有任何变化。

//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)); 

这里的整个方法(没有十六进制编码):

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();
}

现在,虽然有几件事我不满意(例如,这是使用活动而不是服务完成的),但这里的大问题显然是上传图像的问题。我绝不是第一个遇到这个问题的人,那么有人能用java完成这个问题吗?

编辑1

在解决手头的问题方面没有取得任何进展,但创建了一个变通方法,对有同样问题的人来说可能很好。汤博乐提供通过邮件发帖,你可以编程android在后台发送电子邮件,如图所示。这非常有效,但你需要要求用户提供他们的邮件帐户数据和要发布的汤博乐邮件地址。

编辑2

多年过去了,使用电子邮件不再是一种简单的方式。有了jumblr,Java终于有了一个好的API,可以在android上工作了。OAuth身份验证并不有趣(从来都不是),但一旦你通过了这一点,它就非常棒了。

现在,从技术上讲,如何进行身份验证的问题不属于这里,但这是我的一个太长的问题,所以我会在这里粘贴一些代码,如果你不感兴趣,就跳过它。

这使用了一个名为jumblr-0.0.10-jar-with-dependences.jar的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;
}

}

WebViewFragment看起来像这样:

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();
}
}
}
}

为什么不使用Jumblr作为汤博乐的官方Java客户端呢。

谨致问候。

您可以使用jumblr-汤博乐java客户端轻松完成此操作

JumblrClient client = new JumblrClient(Constant.CONSUMER_KEY,Constant.CONSUMER_SECRET);
client.setToken(preferences.getString("token",null), preferences.getString("token_secret", null));
PhotoPost pp = client.newPost(client.user().getBlogs().get(0).getName(),PhotoPost.class);
pp.setCaption(caption);
// pp.setLinkUrl(link);
// pp.setSource(mImage); // String URL
pp.setPhoto(new Photo(imgFile));
pp.save();

这对我有用…

nameValuePairs.add(new BasicNameValuePair(URLEncoder
.encode("type", "UTF-8"),
URLEncoder.encode("photo", "UTF-8")));
Log.e("Tumblr", "Image shareing file path" + filePath);
nameValuePairs.add(new BasicNameValuePair("caption", caption));
nameValuePairs.add(new BasicNameValuePair("source", filePath));`

其中filePath是http url。

我使用了multipart公共类VideoUploader扩展AsyncTask{

ProgressDialog progressDialog;
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
progressDialog = ProgressDialog.show(RecordingActivity.this, "",
"Uploading video.. ");
super.onPreExecute();
}
@Override
protected JSONObject doInBackground(String... params) {
JSONObject jsonObject = null;
StringBuilder builder = new StringBuilder();
try {
String url = UrlConst.VIDEO_URL;
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
FileBody filebodyVideo = new FileBody(new File(params[0]));
StringBody title = new StringBody("uploadedfile: " + params[0]);
StringBody description = new StringBody(
"This is a video of the agent");
// StringBody code = new StringBody(realtorCodeStr);
MultipartEntity reqEntity = new MultipartEntity();
reqEntity.addPart("uploadedfile", filebodyVideo);
reqEntity.addPart("title", title);
reqEntity.addPart("description", description);
// reqEntity.adddPart("code", code);
httppost.setEntity(reqEntity);
// DEBUG
System.out.println("executing request "
+ httppost.getRequestLine());
HttpResponse response = httpclient.execute(httppost);
HttpEntity resEntity = response.getEntity();
// DEBUG
StatusLine status = response.getStatusLine();
int statusCode = status.getStatusCode();
System.out.println(response.getStatusLine());
if (resEntity != null) {
System.out.println(EntityUtils.toString(resEntity));
} // end if
if (resEntity != null) {
resEntity.consumeContent();
} // end if
if (statusCode == 200) {
InputStream content = resEntity.getContent();
BufferedReader reader = new BufferedReader(
new InputStreamReader(content));
String line;
while ((line = reader.readLine()) != null) {
builder.append(line);
}
jsonObject = new JSONObject(builder.toString());
return jsonObject;
} else {
Log.e(LoginActivity.class.toString(),
"Failed to download file");
}
httpclient.getConnectionManager().shutdown();
} catch (Exception e) {
// TODO: handle exception
}
return null;
}
@Override
protected void onPostExecute(JSONObject result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
progressDialog.dismiss();
if (result != null) {
try {
JSONObject jsonObject = result
.getJSONObject(ParsingTagConst.COMMANDRESULT);
String strSuccess = jsonObject
.getString(ParsingTagConst.SUCCESS);
String responseString = jsonObject
.getString(ParsingTagConst.RESPONSE_STRING);
Toast.makeText(RecordingActivity.this, "" + responseString,
Toast.LENGTH_LONG).show();
if (strSuccess.equals("1")) {
// get here your response
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
}

enter code here

我使用了以下方法。你可以试试这个。

//paramString="要放入标题的文本">

private void postPhotoTumblr(String uploadedImagePhotoUrl, String paramString)
{
CommonsHttpOAuthConsumer localCommonsHttpOAuthConsumer = getTumblrConsumer();
String str1 = "logged in username";
String encodedImage = uploadedImagePhotoUrl;
DefaultHttpClient localDefaultHttpClient = new DefaultHttpClient();
HttpPost localHttpPost = new HttpPost("http://api.tumblr.com/v2/blog/" + str1 + ".tumblr.com/post");
try
{
ArrayList localArrayList = new ArrayList();
localArrayList.add(new BasicNameValuePair("type", "photo"));
BasicNameValuePair localBasicNameValuePair = new BasicNameValuePair("caption", paramString);
localArrayList.add(localBasicNameValuePair);
localArrayList.add(new BasicNameValuePair("data",encodedImage));
UrlEncodedFormEntity localUrlEncodedFormEntity = new UrlEncodedFormEntity(localArrayList);
localHttpPost.setEntity(localUrlEncodedFormEntity);
localCommonsHttpOAuthConsumer.sign(localHttpPost);
InputStream localInputStream = localDefaultHttpClient.execute(localHttpPost).getEntity().getContent();
InputStreamReader localInputStreamReader = new InputStreamReader(localInputStream);
BufferedReader localBufferedReader = new BufferedReader(localInputStreamReader);
StringBuilder localStringBuilder = new StringBuilder();
while (true)
{
String str2 = localBufferedReader.readLine();
if (str2 == null)
{
Log.i("DATA post resp", localStringBuilder.toString());
break;
}
localStringBuilder.append(str2);
}
}
catch (ClientProtocolException localClientProtocolException)
{
localClientProtocolException.printStackTrace();
}
catch (IOException localIOException)
{
localIOException.printStackTrace();
}
catch (OAuthMessageSignerException localOAuthMessageSignerException)
{
localOAuthMessageSignerException.printStackTrace();
}
catch (OAuthExpectationFailedException localOAuthExpectationFailedException)
{
localOAuthExpectationFailedException.printStackTrace();
}
catch (OAuthCommunicationException localOAuthCommunicationException)
{
localOAuthCommunicationException.printStackTrace();
}
}

编辑:首先将图片上传到Web服务器,然后获取Url并尝试使用上传的Url或文件路径进行发布。它肯定会很好用…:)

最新更新