SQLの窓

2013年07月18日


VS2010(C#) Form : POST statuses/update_with_media で画像を伴った Twitter 投稿

VS 2010 用のテンプレートです。

SkyDrive へ移動


Twitter API : POST statuses/update_with_media

❶ API 1.1 での URL は、https://api.twitter.com/1.1/statuses/update_with_media.json です。
❷ 認証用のデータは、oauth_* のみ使って、status は使用しません
❸ 日本語投稿部分は、URLエンコードでは無く、UTF-8 で出力します
❹ UI スレッドへのアクセスは、SynchronizationContext を使用しています
❺ カスタムイベントで、投稿結果の JSON を取得します( UI スレッド )

生データをチェックしたい場合、AN HTTPD では、トレースを出力するようにして、http://localhost/test.php を投稿先とし、test.php には OK とのみ書いておきます。

Progress が必要な場合は、wr.AllowWriteStreamBuffering = false; と wr.SendChunked = true; を実行して、ファイルを書き込むループ内で処理するか、そこから外部へのイベントを作成すれば良いと思います。

ここでは、409600 というバッファのサイズにあまり意味はありませんが、どの程度のサイズのファイルのアップロードの Progress をテストするかによって、考慮する必要が出て来ると思います。しかし、実装段階での効率の良いサイズとしては、大きすぎず、小さすぎず程度でいいと思います。

Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using LBOX_Tool;
using System.Net;

namespace Twitter_Post_with_Media_test1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.FileName = "";
            ofd.Filter = "JPEG|*.jpg;*.jpeg";
            ofd.FilterIndex = 0;

            if (ofd.ShowDialog() != DialogResult.OK)
            {
                return;
            }

            VS2010_Twitter twitter =
                new VS2010_Twitter(
                    "Consumer key",
                    "Consumer secret",
                    "Access token",
                    "Access token secret"
                );

            twitter.TwitterImageUploadResult += (object _sender, VS2010_Twitter.TwitterImageUploadArgs _e) =>
            {
                this.textBox2.Text = _e.Message;
            };

            twitter.Tweet(this.textBox1.Text, ofd.FileName);

        }
    }
}



VS2010_Twitter.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.Diagnostics;
using System.IO;
using System.Threading;

namespace LBOX_Tool
{
    class VS2010_Twitter
    {
        private string _consumer_key;
        private string _consumer_secret;
        private string _token;
        private string _secret;
        private SynchronizationContext sc = null;

        // API 1.1 における最新の URL
        private string _tweet_api = "https://api.twitter.com/1.1/statuses/update_with_media.json";

        // イベント引き渡し用クラス
        private class MyParam
        {
            public WebRequest wr;
            public string image_path;
            public string strBoundary;
            public string text;
        }

        public VS2010_Twitter(
            string consumer_key,
            string consumer_secret,
            string token,
            string secret
            )
        {
            _consumer_key = consumer_key;
            _consumer_secret = consumer_secret;
            _token = token;
            _secret = secret;
        }

        public void Tweet(string text, string path)
        {
            // UI スレッドに戻る為のコンテキスト
            sc = SynchronizationContext.Current;

            // ソートされるリスト
            SortedList<string, string> sl = new SortedList<string, string>();
            sl.Add("oauth_consumer_key", _consumer_key);
            sl.Add("oauth_nonce", Nonce());
            sl.Add("oauth_signature_method", "HMAC-SHA1");
            sl.Add("oauth_timestamp", TimeStamp());
            sl.Add("oauth_token", _token);
            sl.Add("oauth_version", "1.0");
            // 画像投稿では、以上で認証用のデータを作成する( status は使用できない )

            // http ヘッダ用シグネチャ作成
            string work = "";
            foreach (KeyValuePair<string, string> kvp in sl)
            {
                if (work != "")
                {
                    work += "&";
                }
                work += kvp.Key + "=" + kvp.Value;
            }

            string work2 = "";
            // メソッド
            work2 += "POST" + "&";
            // API URL
            work2 += Uri.EscapeDataString(_tweet_api) + "&";
            // Oauth + データ
            work2 += Uri.EscapeDataString(work);

            // OAuth tool チェック用
            Debug.WriteLine(work2);

            string oauth_signature = Signature(work2);

            // ヘッダ情報を作成
            work = "";
            foreach (KeyValuePair<string, string> kvp in sl)
            {
                // oauth_* のみを使用する
                if (work != "")
                {
                    if (kvp.Key.Substring(0, 6) == "oauth_")
                    {
                        work += ", ";
                    }
                }
                if (kvp.Key.Substring(0, 6) == "oauth_")
                {
                    work += kvp.Key + "=" + Dd(kvp.Value);
                }
            }
            // シグネチャを追加( ヘッダーはソートの必要は無い )
            work += ", oauth_signature=" + Dd(Uri.EscapeDataString(oauth_signature));

            // OAuth tool チェック用
            Debug.WriteLine(work);

            // 投稿準備(1)
            string strBoundary = DateTime.Now.Ticks.ToString("x");
            HttpWebRequest wr = (HttpWebRequest)HttpWebRequest.Create(_tweet_api);
            // 生データをチェックしたい場合
            // HttpWebRequest wr = (HttpWebRequest)HttpWebRequest.Create("http://localhost/test.php");
            // Progress が必要な場合
            //wr.AllowWriteStreamBuffering = false;
            //wr.SendChunked = true;

            // HttpWebRequest の基本部分の設定
            wr.Method = "POST";
            wr.ContentType = "multipart/form-data; boundary=---" + strBoundary;
            wr.Headers["Authorization"] = "OAuth " + work;

            // ストリームへの書き込みイベント
            AsyncCallback writeCallBack = new AsyncCallback(WriteCallBack);
            MyParam myParam = new MyParam() { wr = wr, image_path = path, strBoundary = strBoundary, text = text };
            // 呼び出し
            IAsyncResult iar1 = wr.BeginGetRequestStream(writeCallBack, myParam);

        }

        // 書き込み
        public void WriteCallBack(IAsyncResult ar)
        {
            HttpWebRequest req = (HttpWebRequest)(ar.AsyncState as MyParam).wr;
            string path = (ar.AsyncState as MyParam).image_path;
            string strBoundary = (ar.AsyncState as MyParam).strBoundary;
            string text = (ar.AsyncState as MyParam).text;

            // 書き込み用のストリーム
            Stream sw = req.EndGetRequestStream(ar);

            // ▼ 以下、Twitter のドキュメントサンプル
            //--cce6735153bf14e47e999e68bb183e70a1fa7fc89722fc1efdf03a917340
            //Content-Disposition: form-data; name="status"
            //
            //Hello 2012-09-07 15:51:41.375247 -0700 PDT!
            //--cce6735153bf14e47e999e68bb183e70a1fa7fc89722fc1efdf03a917340
            //Content-Type: application/octet-stream
            //Content-Disposition: form-data; name="media[]"; filename="media.png"
            //...
            //--cce6735153bf14e47e999e68bb183e70a1fa7fc89722fc1efdf03a917340--

            // 書き込み処理
            Encoding encoding = Encoding.ASCII;
            // セクション(1) / 日本語投稿本文
            Byte[] content = encoding.GetBytes("-----" + strBoundary + "\r\n");
            sw.Write(content, 0, content.Length);

            content = encoding.GetBytes("Content-Disposition: form-data; name=\"status\"\r\n\r\n");
            sw.Write(content, 0, content.Length);

            content = Encoding.GetEncoding("UTF-8").GetBytes(text + "\r\n");
            sw.Write(content, 0, content.Length);

            // セクション(2) / 生バイナリ画像部分
            content = encoding.GetBytes("-----" + strBoundary + "\r\n");
            sw.Write(content, 0, content.Length);

            content = encoding.GetBytes("Content-Type: application/octet-stream\r\n");
            sw.Write(content, 0, content.Length);

            content = encoding.GetBytes("Content-Disposition: form-data; name=\"media[]\"; filename=\"media.jpg\"\r\n\r\n");
            sw.Write(content, 0, content.Length);

            // ファイルのバイナリデータ( バッファサイズの大きさにあまり意味はありません )
            FileStream fs = new FileStream(path, FileMode.Open);
            int buffer_len = 409600;
            long length = 0;
            int read_size = 0;
            byte[] file_data = new byte[buffer_len];
            while ((read_size = fs.Read(file_data, 0, buffer_len)) > 0)
            {
                length += read_size;
                Debug.WriteLine("writing...." + length);
                sw.Write(file_data, 0, read_size);
                if (read_size < buffer_len)
                {
                    break;
                }
            }
            fs.Close();
            fs.Dispose();

            // multipart の終了
            content = encoding.GetBytes("\r\n-----" + strBoundary + "--\r\n");
            sw.Write(content, 0, content.Length);

            sw.Close();
            sw.Dispose();

            // 最終的なレスポンスの取得
            AsyncCallback readCallBack = new AsyncCallback(this.ReadCallBack);
            IAsyncResult iar2 = req.BeginGetResponse(readCallBack, req);

        }

        // 読み込み
        public void ReadCallBack(IAsyncResult ar)
        {
            string result = "";

            HttpWebRequest req = (HttpWebRequest)ar.AsyncState;
            HttpWebResponse response = null;
            // ここで時々接続が切られたりします 
            try
            {
                response = (HttpWebResponse)req.EndGetResponse(ar);

                Encoding enc = System.Text.Encoding.GetEncoding("UTF-8");
                StreamReader sr = new StreamReader(response.GetResponseStream(), enc);

                result = sr.ReadToEnd();
                Debug.WriteLine(result);
                sr.Close();
                sr.Dispose();
            }
            catch (Exception ex)
            {
                result = ex.Message;
                Debug.WriteLine(result);
            }

            // 内部のイベントの呼び出し( 最終的なイベントを UI スレッドで実行させる )
            sc.Post((object state) => { OnTwitterImageUploadResult(new TwitterImageUploadArgs(result)); }, null);

        }

        // ***************************************************
        // カスタムイベント用引数
        // ***************************************************
        public class TwitterImageUploadArgs : EventArgs
        {
            // 引数の保存エリア
            private string message;
            // コンストラクタ
            public TwitterImageUploadArgs(string s)
            {
                message = s;
            }

            // 引数取り出し用のプロパティ
            public string Message
            {
                get { return message; }
            }
        }

        // ***************************************************
        // カスタムイベントハンドラの定義 ( EventHandler<T> )
        // ***************************************************
        public event EventHandler<TwitterImageUploadArgs> TwitterImageUploadResult;

        // ***************************************************
        // 外部へイベントを発行する為の内部メソッドの定義( virtual )
        // ***************************************************
        protected virtual void OnTwitterImageUploadResult(TwitterImageUploadArgs e)
        {
            EventHandler<TwitterImageUploadArgs> handler = TwitterImageUploadResult;

            // イベントが外部で実装されている場合、そのイベントを呼び出す
            if (handler != null)
            {
                handler(this, e);
            }
        }

        // ダブルクォートで挟む
        private string Dd(string base_string)
        {
            return "\"" + base_string + "\"";
        }

        private string Nonce()
        {
            Random rand = new Random();
            int nonce = rand.Next(1000000000);
            return nonce.ToString();
        }

        // タイムスタンプ
        private string TimeStamp()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        // シグネチャ
        private string Signature(string target)
        {
            string work = _consumer_secret + "&" + _secret;
            byte[] bin = Encoding.UTF8.GetBytes(target);

            HMACSHA1 hmacsha1 = new HMACSHA1();
            hmacsha1.Key = Encoding.UTF8.GetBytes(work);
            byte[] hash = hmacsha1.ComputeHash(bin);

            return Convert.ToBase64String(hash);
        }

    }

}

関連する記事Twitter API 1.1 : POST statuses/update_with_media / 画像付き投稿 -- の注意事項Windows Phone から Facebook に画像アップロード


【VS(C#)の最新記事】
posted by lightbox at 2013-07-18 04:37 | VS(C#) | このブログの読者になる | 更新情報をチェックする
container 終わり



フリーフォントで簡単ロゴ作成
フリーフォントでボタン素材作成
フリーフォントで吹き出し画像作成
フリーフォントではんこ画像作成
ほぼ自由に利用できるフリーフォント
フリーフォントの書体見本とサンプル
画像を大きく見る為のウインドウを開くボタンの作成

CSS ドロップシャドウの参考デモ
イラストAC
ぱくたそ
写真素材 足成
フリーフォント一覧
utf8 文字ツール
右サイド 終わり
base 終わり