Skip to content

Instantly share code, notes, and snippets.

@kamend
Created May 24, 2014 07:06
Show Gist options
  • Save kamend/85c37cb2e6260a2ecf2d to your computer and use it in GitHub Desktop.
Save kamend/85c37cb2e6260a2ecf2d to your computer and use it in GitHub Desktop.
How post an image to Tumblr via their V2 API, inside Unity
using UnityEngine;
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Globalization;
using System.Security.Cryptography;
using System.IO;
public class URLUtils {
private static string _UnreservedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
public static string URLEncodeBytes(byte[] value, bool encodePercent)
{
var osb = new StringBuilder(value.Length * 3);
for (var x = 0; x < value.Length; x++)
{
var b = value[x];
if( (char)b == '%') {
osb.Append ("%25");
} else if ((_UnreservedChars.IndexOf((char)b) == -1) && ((char)b) != '~' && (((char)b) != '%'))
{
osb.AppendFormat("%{0:X2}", b);
}
else
{
osb.Append((char)b);
}
}
if(encodePercent) {
osb = osb.Replace("%", "%25");// Revisit to encode actual %'s
}
return osb.ToString();
}
}
public class TumblrOAuth : MonoBehaviour {
private string consumerKey = "";
private string consumerSecret = "";
private string accessToken = "";
private string accessTokenSecret = "";
void Start () {
string imagePath = "file://"+Application.dataPath + "/../image.png";
StartCoroutine(UploadImage("xxxx.tumblr.com", imagePath));
}
private static string GenerateTimeStamp()
{
// Default implementation of UNIX time of the current UTC time
TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
return Convert.ToInt64(ts.TotalSeconds, CultureInfo.CurrentCulture).ToString(CultureInfo.CurrentCulture);
}
private static string GenerateNonce()
{
// Just a simple implementation of a random number between 123400 and 9999999
return new System.Random().Next(123400, int.MaxValue).ToString();
}
IEnumerator UploadImage(string blogAddress, string imagePath) {
string uri = string.Format("http://api.tumblr.com/v2/blog/{0}/post",blogAddress);
// download image
WWW img = new WWW(imagePath);
yield return img;
// signature
Dictionary<string, string> options = new Dictionary<string, string>();
string timestamp = GenerateTimeStamp();
string nonce = timestamp+GenerateNonce();
string method = "POST";
options.Add("oauth_nonce", nonce);
options.Add("oauth_timestamp", timestamp);
var stringBuilder = new StringBuilder();
stringBuilder.Append(method+"&");
stringBuilder.Append(Uri.EscapeDataString(uri));
stringBuilder.Append("&");
//the key value pairs have to be sorted by encoded key
var dictionary = new SortedDictionary<string, string>
{
//oauth
{"oauth_token", accessToken},
{"oauth_consumer_key", consumerKey},
{"oauth_timestamp", timestamp},
{"oauth_nonce", nonce},
{"oauth_signature_method", "HMAC-SHA1"},
{"oauth_version", "1.0"},
// post params
{"type","photo"},
{"data[0]",""}
};
// encode all params
foreach (var keyValuePair in dictionary)
{
byte[] bytesValue = System.Text.Encoding.ASCII.GetBytes(keyValuePair.Value);
string encodedValue;
if(keyValuePair.Key.IndexOf("data") > -1) {
// encode image data directly from the image bytes
// when creating the signature the % must be changed with %25, hence the true param for URLEncodeBytes
encodedValue = URLUtils.URLEncodeBytes(img.bytes, true);
} else {
encodedValue = URLUtils.URLEncodeBytes(bytesValue,false);
}
byte[] bytesKey = System.Text.Encoding.ASCII.GetBytes(keyValuePair.Key);
string encodedKey = URLUtils.URLEncodeBytes(bytesKey,false);
//append a = between the key and the value and a & after the value
stringBuilder.Append(string.Format("{0}%3D{1}%26", encodedKey, encodedValue));
}
// the holy signature base!
string signatureBaseString = stringBuilder.ToString().Substring(0, stringBuilder.Length - 3);
string signatureKey =
Uri.EscapeDataString(consumerSecret) + "&" +
Uri.EscapeDataString(accessTokenSecret);
var hmacsha1 = new HMACSHA1(
System.Text.Encoding.ASCII.GetBytes(signatureKey)
);
// teh signature string
string signatureString = Convert.ToBase64String(
hmacsha1.ComputeHash(
System.Text.Encoding.ASCII.GetBytes(signatureBaseString)));
signatureString = Uri.EscapeDataString(signatureString);
// the authorize header
string auth = "OAuth oauth_token=\""+accessToken+"\", oauth_consumer_key=\""+consumerKey+"\", oauth_signature_method=\"HMAC-SHA1\", oauth_timestamp=\""+timestamp+"\", oauth_nonce=\""+nonce+"\", oauth_version=\"1.0\", oauth_signature=\""+signatureString+"\"";
// the post data
// we have to build it ourselves, because if we use WWWForm, it will enforce it's own way of
// encoding the image data, and it will break things
// we don't change % to %25 when adding the post data
string imgDataEncoded = URLUtils.URLEncodeBytes(img.bytes, false);
StringBuilder postData = new StringBuilder();
postData.Append("data%5B0%5D=");
postData.Append (imgDataEncoded);
postData.Append ("&type=photo");
// send request
Hashtable headers = new Hashtable();
headers["Authorization"] = auth;
headers["Content-type"] = "application/x-www-form-urlencoded";
WWW web = new WWW(uri, System.Text.Encoding.ASCII.GetBytes(postData.ToString()), headers);
yield return web;
if (!string.IsNullOrEmpty(web.error))
{
Debug.Log(string.Format("GetAccessToken - failed. error : {0}", web.error));
} else {
Debug.Log (web.text);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment