、と巷で評判になってましたが、Google 側がさすがにまずいと思ったらしく、表現が変わっていました。
( つい数時間前まで『AdSenseの終了』)
『Google サイトでの AdSense 掲載終了について』
英語ページに変更すると、いまだに『Sunsetting AdSense』となっていますが、そちらでは『 AdSense on any of your Google Sites』なんで間違う人もいないと思います。実際、Google.com で検索すると、焦点は『Googleサイトでもう収入は得られないのか?』というものでした。でも、日本じゃ騒然とするのも仕方無いですね。
だいたい、Google のドキュメントは、英語ページと日本語ページで違う事も多く、ある英単語に対して時期が違うと違う日本語の単語になっていたりする事もあります。ただ、たいていにおいて、『ん?』となる程度で問題になった事はあまりないでしょうが、今回はさすがに対応したみたいですね(笑)
そう言えば、最後の1文は前は無かったはずです。
『Google サイト以外でお使いの AdSense への影響はなく、通常通りご利用いただけます。』
2013年07月26日
まぎらわしい『AdSenseの終了』を知らせる『日本語ページ』
2013年07月24日
Trigger Rally(WebGL+Three.js) のコースを無視してひたすら悪路を走りまくる
Bandicam で録画しました。 ( 動画も便利ですが、静止画もさらに便利ですね ) WebGL+Three.js なので、Google Chrome で実行しています。Trigger Rally は、Three.js をエンジンとして CoffeeScript で書かれています。 結論から言うと、ヒマつぶしに持ってこいです。 上下矢印キー : 前進・後退 左右矢印キー : ハンドル スペースキー : ハンドブレーキ C キー : カメラビューの変更 R キー : リスタート 以上の操作で、ただひたすらにどこまても悪路を走り続ける事ができます。本来は、コースを作ってタイムトライアルをするのですが、コースをカスタマイズしたりデモコース以外を走ったりするには PayPal で 5ドル程度を支払うと可能になります。( イマイチ詳細が良く解りません ) この時、車の種類が2台増えるのですが・・・・あまり面白く無いのでフリーで遊べばいいと思います。( 検証の為に 5ドル払いました ) 羽の生えた車を使って大空をグライダー飛行したり、天空のダートコースを走ったりできるのですが・・・たぶんすぐ飽きちゃうと思います。
Google Chrome を強制終了すると、後で『開いていたページを復元』できますが、ログイン状態も復元してしまいます。
しごく当たり前の事ですが、自宅以外のコンピュータでどこかのサービースへログインしたままシャットダウンまたはログオフしてしまうと、次に Google Chrome を実行した人がそのサービスへのログイン状態のまま作業ができてしまいます。
2013年07月18日
VS2010(C#) Form : Google API でログインして Google ドライブにアップロード
ダウンロードする zip は、VS2010 用のテンプレートです SkyDrive へ移動 ダイアログを表示して、ログインした後ダイアログを閉じて、フォームより画像をアップロードします( 他の種類のファイルをアップロードするには、コードを変更して MIME と拡張子の関係を設定して下さい ) ダイアログ( Form2 ) から Form1 のコントロールを参照できるように、該当するコントロールの modifiers を Public に変更しています。 Client ID と Client secret と Redirect URIs が必要です( APIs Console ) 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 System.Net; namespace Google_Drive_Upload4 { public partial class Form1 : Form { // ******************************************* // 以下の3つの static な文字列を設定して下さい // ******************************************* public static string client_id = ""; public static string client_secret = ""; // ▼ 登録しているものを設定しないとエラーになります public static string redirect_uri = ""; // ログインで取得する値 public string access_token = null; public string token_type = null; // ログインダイアログ public Form2 login = null; // ここで使用する為に static にしています public VS2010_GoogleDrive vgd = new VS2010_GoogleDrive( client_id, client_secret, redirect_uri ); public Form1() { InitializeComponent(); } // *************************************************************** // 投稿 // *************************************************************** private void button1_Click(object sender, EventArgs e) { // ダイアログを開く準備 this.openFileDialog1.Filter = "JPEG|*.jpg*;*.jpg"; this.openFileDialog1.FilterIndex = 0; this.openFileDialog1.FileName = ""; // ダイアログを開く DialogResult dr = this.openFileDialog1.ShowDialog(); if (dr == System.Windows.Forms.DialogResult.OK) { // 実際は、拡張子によって、MIME を変更する vgd.Upload(openFileDialog1.FileName, "image/jpeg", token_type, access_token); vgd.GoogleUploadResult += (object _sender, VS2010_GoogleDrive.GoogleUploadArgs _e) => { this.textBox1.Text = _e.Message; }; } } // *************************************************************** // ログインダイアログを開く // *************************************************************** private void button2_Click(object sender, EventArgs e) { login = new Form2(); login.ShowDialog(this); } } }
Form2.cs WebBrowser のみを配置したダイアログです
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 System.Diagnostics; using System.Net; using Newtonsoft.Json.Linq; namespace Google_Drive_Upload4 { public partial class Form2 : Form { // 親フォーム参照用の変数 private Form1 owner = null; private WebClient webClient = null; // アクセストークン取得用の URL private string _auth2_url = "https://accounts.google.com/o/oauth2/token"; public Form2() { InitializeComponent(); } // *************************************************************** // 初期処理 // *************************************************************** private void Form2_Load(object sender, EventArgs e) { // 初期処理として、親フォーム参照用の変数をセット owner = this.Owner as Form1; // ログイン用のページを表示 this.webBrowser1.Navigate(new Uri(owner.vgd.LoginUrl)); } // *************************************************************** // code を取得 // *************************************************************** private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e) { string url = e.Url.ToString(); if (url.IndexOf("code=") != -1) { Debug.WriteLine(url); // URL に含まれる code を取得する int cur = url.IndexOf("="); string _code = url.Substring(cur + 1); webClient = new WebClient(); webClient.UploadStringCompleted += new UploadStringCompletedEventHandler(get_token); // POST 用 webClient.Headers["Content-Type"] = "application/x-www-form-urlencoded"; string param = ""; param = "code=" + _code; param += "&grant_type=authorization_code"; param += "&redirect_uri=" + Form1.redirect_uri; param += "&client_id=" + Form1.client_id; param += "&client_secret=" + Form1.client_secret; // 呼び出し webClient.UploadStringAsync(new Uri(_auth2_url), "POST", param); } } // *************************************************************** // 有効なアクセストークンを取得 // *************************************************************** private void get_token(object sender, UploadStringCompletedEventArgs e) { if (e.Error != null) { Debug.WriteLine("get_token:" + e.Error.Message); } else { string json_string = e.Result; Debug.WriteLine(json_string); // Json.NET の処理 JObject data = JObject.Parse(json_string); owner.access_token = data["access_token"].ToString(); owner.token_type = data["token_type"].ToString(); // 全ての処理が終わったのでメインページへ戻る // ( コントロールの modifiers を public にしているので参照可能 ) owner.button1.Enabled = true; owner.textBox1.Enabled = true; webClient.Dispose(); // ダイアログを閉じる this.Close(); } } } }
VS2010_GoogleDrive.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.IO; using System.Diagnostics; using System.Threading; namespace Google_Drive_Upload4 { public class VS2010_GoogleDrive { private string _client_id; private string _client_secret; private string _redirect_uri; private SynchronizationContext sc = null; // ログイン用の URL private string loginUrlBase = "https://accounts.google.com/o/oauth2/auth"; private string responseType = "code"; // https://developers.google.com/drive/training/drive-apps/auth/scopes?hl=ja // https://developers.google.com/drive/v2/reference/files/insert private string scope = "https://www.googleapis.com/auth/drive.file+https://www.googleapis.com/auth/drive+https://www.googleapis.com/auth/drive.appdata"; // アップロード用の URL private string upload_url = "https://www.googleapis.com/upload/drive/v2/files?uploadType=multipart"; // private string upload_url = "http://localhost/test.php"; // イベント引き渡し用クラス private class MyParam { public WebRequest web_request; public string boundary; public string file_path; public string content_type; } public VS2010_GoogleDrive( string client_id, string client_secret, string redirect_uri ) { _client_id = client_id; _client_secret = client_secret; _redirect_uri = redirect_uri; } // *************************************************** // GoogleログインURLを取得する // https://developers.google.com/accounts/docs/OAuth2Login // *************************************************** public string LoginUrl { get { string url = loginUrlBase + "?"; url += "client_id=" + _client_id; url += "&response_type=" + responseType; url += "&scope=" + scope; url += "&redirect_uri=" + _redirect_uri; return url; } } // *************************************************** // アップロード // *************************************************** public void Upload(string path, string contentType, string tokenType, string token) { // UI スレッドに戻る為のコンテキスト sc = SynchronizationContext.Current; HttpWebRequest webRequest = (HttpWebRequest)HttpWebRequest.Create(upload_url); // 経過処理を可能にする為に、キャッシュをしない設定 webRequest.AllowWriteStreamBuffering = false; webRequest.SendChunked = true; webRequest.Method = "POST"; // 地道なアップロード処理の開始 string strBoundary = DateTime.Now.Ticks.ToString("x"); webRequest.ContentType = "multipart/form-data; boundary=" + strBoundary; webRequest.Headers.Add("Authorization", tokenType + " " + token); AsyncCallback writeCallBack = new AsyncCallback(WriteCallBack); MyParam myParam = new MyParam() { web_request = webRequest, boundary = strBoundary, file_path = path, content_type = contentType }; // 要求開始 IAsyncResult iar1 = webRequest.BeginGetRequestStream(writeCallBack, myParam); } // *************************************************** // 書き込み // *************************************************** private void WriteCallBack(IAsyncResult ar) { HttpWebRequest webRequest = (HttpWebRequest)(ar.AsyncState as MyParam).web_request; string strBoundary = (ar.AsyncState as MyParam).boundary; // ファイルのパス string file_path = (ar.AsyncState as MyParam).file_path; // アップロード用のファイル名 string file_name = Path.GetFileName(file_path); string content_type = (ar.AsyncState as MyParam).content_type; Stream binWriter = webRequest.EndGetRequestStream(ar); //--foo_bar_baz //Content-Type: application/json; charset=UTF-8 //{ // "title": "My File" //} //--foo_bar_baz //Content-Type: image/jpeg //JPEG data //--foo_bar_baz-- Encoding encoding = Encoding.ASCII; Byte[] content = encoding.GetBytes("--" + strBoundary + "\r\n"); binWriter.Write(content, 0, content.Length); content = encoding.GetBytes("Content-Type: application/json; charset=UTF-8\r\n\r\n"); binWriter.Write(content, 0, content.Length); // JSON フォーマットによるファイルの指定 content = encoding.GetBytes("{\r\n"); binWriter.Write(content, 0, content.Length); // ファイル名は UTF-8 で content = Encoding.GetEncoding("UTF-8").GetBytes(" \"title\": \"" + file_name + "\"\r\n"); binWriter.Write(content, 0, content.Length); content = encoding.GetBytes("}\r\n"); binWriter.Write(content, 0, content.Length); content = encoding.GetBytes("--" + strBoundary + "\r\n"); binWriter.Write(content, 0, content.Length); content = encoding.GetBytes("Content-Type: " + content_type + "\r\n\r\n"); binWriter.Write(content, 0, content.Length); // ファイルのバイナリデータ FileStream fs = new FileStream(file_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); binWriter.Write(file_data, 0, read_size); if (read_size < buffer_len) { break; } } fs.Close(); Debug.WriteLine("終了"); content = encoding.GetBytes("\r\n--" + strBoundary + "--\r\n"); binWriter.Write(content, 0, content.Length); binWriter.Close(); AsyncCallback readCallBack = new AsyncCallback(this.ReadCallBack); IAsyncResult iar2 = webRequest.BeginGetResponse(readCallBack, webRequest); } // *************************************************** // 読み込み // *************************************************** private void ReadCallBack(IAsyncResult ar) { HttpWebRequest webRequest = (HttpWebRequest)ar.AsyncState; HttpWebResponse response = (HttpWebResponse)webRequest.EndGetResponse(ar); Encoding enc = System.Text.Encoding.GetEncoding("UTF-8"); StreamReader streamReader = new StreamReader(response.GetResponseStream(), enc); string str = streamReader.ReadToEnd(); streamReader.Close(); Debug.WriteLine(str); // 内部のイベントの呼び出し( 最終的なイベントを UI スレッドで実行させる ) sc.Post((object state) => { OnGoogleUploadResult(new GoogleUploadArgs(str)); }, null); } // *************************************************** // カスタムイベント用引数 // *************************************************** public class GoogleUploadArgs : EventArgs { // 引数の保存エリア private string message; // コンストラクタ public GoogleUploadArgs(string s) { message = s; } // 引数取り出し用のプロパティ public string Message { get { return message; } } } // *************************************************** // カスタムイベントハンドラの定義 ( EventHandler<T> ) // *************************************************** public event EventHandler<GoogleUploadArgs> GoogleUploadResult; // *************************************************** // 外部へイベントを発行する為の内部メソッドの定義( virtual ) // *************************************************** protected virtual void OnGoogleUploadResult(GoogleUploadArgs e) { EventHandler<GoogleUploadArgs> handler = GoogleUploadResult; // イベントが外部で実装されている場合、そのイベントを呼び出す if (handler != null) { handler(this, e); } } } }
コメントの、"http://localhost/test.php" は、生の投稿データを確認する為に使用しています( トレース ) 関連する記事 VS2010(C#) Form : POST statuses/update_with_media で画像を伴った Twitter 投稿
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 に画像アップロード
2013年07月14日
Formアプリ(C#) でFacebookにログインして60日間のアクセストークンを取得して投稿
JSON の処理は行っていませんが、Json.NET がおすすめです 投稿ボタンとテキストエリアは、コントロールの modifiers を public にしているので Form2 より参照可能です。 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 System.Net; namespace Facebook_POST { public partial class Form1 : Form { public static string app_id = "App ID"; public static string app_secret = "App Secret"; public string url_api = "https://www.facebook.com/dialog/oauth/?redirect_uri=http://www.facebook.com/connect/login_success.html&response_type=token&client_id="+app_id+"&scope=user_about_me,user_photos,read_stream,publish_stream"; public string access_token = null; public Form2 login = null; public Form1() { InitializeComponent(); } // *************************************************************** // 投稿 // *************************************************************** private void button1_Click(object sender, EventArgs e) { VS2010_Facebook facebook = new VS2010_Facebook( app_id, app_secret, access_token ); // ただ投稿するだけなら twitter.Tweet(this.textBox1.Text); facebook.Post(this.textBox1.Text, (object _sender, UploadStringCompletedEventArgs _e) => { if (_e.Error == null) { // JSON String stringResult = _e.Result; MessageBox.Show(_e.Result); } else { MessageBox.Show("通信エラーが発生しました。\r\n" + _e.Error.Message); } }); } // *************************************************************** // ログインダイアログを開く // *************************************************************** private void button2_Click(object sender, EventArgs e) { login = new Form2(); login.ShowDialog(this); } } }
ログインは、WebBrowser コントロールで行います。上の画像では、フォームを親コンテナとしてドッキングしています。実際には、ログイン済みなのかまたは、アクセストークンを保存して使う等の実装が必要ですが、ここでは必ずログインし、ログインが成功する事を前提として、Form1 から投稿を行うようになっています。 Form2.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 System.Diagnostics; using System.Net; namespace Facebook_POST { public partial class Form2 : Form { // 親フォーム参照用の変数 private Form1 owner = null; public Form2() { InitializeComponent(); } // *************************************************************** // 初期処理 // *************************************************************** private void Form2_Load(object sender, EventArgs e) { // 初期処理として、親フォーム参照用の変数をセット owner = this.Owner as Form1; // ログイン用のページを表示 this.webBrowser1.Navigate(new Uri(owner.url_api)); } // *************************************************************** // 2時間有効なアクセストークンを取得 // *************************************************************** private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e) { string url = e.Url.ToString(); if (url.IndexOf("access_token") != -1) { Debug.WriteLine(url); string target_line = url.Substring(url.IndexOf("access_token")); string[] separators = new string[1]; separators[0] = "&"; string[] part_string = target_line.Split(separators, System.StringSplitOptions.RemoveEmptyEntries); Dictionary<string, string> dic = new Dictionary<string, string>(); separators[0] = "="; string[] part_values = null; part_values = part_string[0].Split(separators, System.StringSplitOptions.RemoveEmptyEntries); dic.Add(part_values[0], part_values[1]); part_values = part_string[1].Split(separators, System.StringSplitOptions.RemoveEmptyEntries); dic.Add(part_values[0], part_values[1]); // 60日間有効なアクセストークンを取得する為の処理 WebClient webClient = new WebClient(); webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(get_Token60); // 60日間有効なアクセストークンを取得する API url = String.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=fb_exchange_token&fb_exchange_token={2}", Form1.app_id, Form1.app_secret, dic["access_token"]); // 呼び出し webClient.DownloadStringAsync(new Uri(url)); } } // *************************************************************** // 60日間有効なアクセストークンを取得 // *************************************************************** private void get_Token60(object sender, DownloadStringCompletedEventArgs e) { if (e.Error != null) { Debug.WriteLine(e.Error.Message); } else { string new_token_line = e.Result; Debug.WriteLine(new_token_line); string[] separators = new string[1]; separators[0] = "&"; string[] part_string = new_token_line.Split(separators, System.StringSplitOptions.RemoveEmptyEntries); Dictionary<string, string> dic = new Dictionary<string, string>(); separators[0] = "="; string[] part_values = null; part_values = part_string[0].Split(separators, System.StringSplitOptions.RemoveEmptyEntries); dic.Add(part_values[0], part_values[1]); part_values = part_string[1].Split(separators, System.StringSplitOptions.RemoveEmptyEntries); dic.Add(part_values[0], part_values[1]); Debug.WriteLine(dic["access_token"]); owner.access_token = dic["access_token"]; WebClient webClient = new WebClient(); webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(get_UserInfo); string url = String.Format("https://graph.facebook.com/me?access_token={0}", dic["access_token"]); webClient.DownloadStringAsync(new Uri(url)); } } // *************************************************************** // ユーザ情報を取得 // ( 投稿するだけなら必要ありません ) // *************************************************************** private void get_UserInfo(object sender, DownloadStringCompletedEventArgs e) { if (e.Error != null) { Debug.WriteLine(e.Error.Message); } else { Debug.WriteLine(e.Result); // 全ての処理が終わったのでメインページへ戻る // ( コントロールの modifiers を public にしているので参照可能 ) owner.button1.Enabled = true; owner.textBox1.Enabled = true; // ダイアログを閉じる this.Close(); } } } }
VS2010_Facebook.cs このクラスは実際には、POST しているだけです。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; namespace Facebook_POST { class VS2010_Facebook { private string _app_key; private string _app_secret; private string _token; private string _tweet_api = "https://graph.facebook.com/me/feed"; public VS2010_Facebook( string app_key, string app_secret, string token ) { _app_key = app_key; _app_secret = app_secret; _token = token; } public void Post(string text,UploadStringCompletedEventHandler newEvent=null) { WebClient wc = new WebClient(); if (newEvent != null) { wc.UploadStringCompleted += newEvent; } wc.Headers["Content-Type"] = "application/x-www-form-urlencoded"; // 投稿 wc.UploadStringAsync(new Uri(_tweet_api), "POST", "message=" + Uri.EscapeDataString(text) + "&access_token=" + _token ); } } }
Seesaa の各ページの表示について
Seesaa の 記事がたまに全く表示されない場合があります。その場合は、設定> 詳細設定> ブログ設定 で 最新の情報に更新の『実行ボタン』で記事やアーカイブが最新にビルドされます。 Seesaa のページで、アーカイブとタグページは要注意です。タグページはコンテンツが全く無い状態になりますし、アーカイブページも歯抜けページはコンテンツが存在しないのにページが表示されてしまいます。 また、カテゴリページもそういう意味では完全ではありません。『カテゴリID-番号』というフォーマットで表示されるページですが、実際存在するより大きな番号でも表示されてしまいます。 ※ インデックスページのみ、実際の記事数を超えたページを指定しても最後のページが表示されるようです 対処としては、このようなヘルプ的な情報を固定でページの最後に表示するようにするといいでしょう。具体的には、メインの記事コンテンツの下に『自由形式』を追加し、アーカイブとカテゴリページでのみ表示するように設定し、コンテンツを用意するといいと思います。 ※ エキスパートモードで表示しています アーカイブとカテゴリページはこのように簡単に設定できますが、タグページは HTML 設定を直接変更して、以下の『タグページでのみ表示される内容』の記述方法で設定する必要があります<% if:page_name eq 'archive' -%> アーカイブページでのみ表示される内容 <% /if %> <% if:page_name eq 'category' -%> カテゴリページでのみ表示される内容 <% /if %> <% if:page_name eq 'tag' -%> タグページでのみ表示される内容 <% /if %>この記述は、以下の場所で使用します
|