俺氏、本を読む

30歳になるまでに本を読んで勉強しようかと。主に啓発、お金についての本を読むつもり。一応プログラマーなのでその辺のことも。あと、せどり(転売)の仕入れ見込み商品をリサーチして仕入先と一緒に投稿します

【C#】AtomPubでライブドアブログに記事と画像を投稿するサンプル

C#ライブドアブログに記事と画像を投稿するサンプルコードです。
実際にこのコードで投稿してるブログはアダルトなので紹介できないですけどw
 
ちなみに使ってるVSのバージョンと.NET Frameworkのバージョンは以下です。
Microsoft Visual Studio Express 2013 for Windows Desktop
.NET Framework 4.5
 
プロジェクトの種類はコンソールアプリケーション。
プロジェクトの参照設定に「System.Web」「System.Drawing」を追加する必要があります。
エラーハンドリングとかは適当で動作確認もしてませんwww
動かなくても惜しいところまではいけるはずw
 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
using System.IO;
using System.Net;
using System.Web;
using System.Xml;
using System.Security.Cryptography;

namespace ConsoleApplication1
{
    class Program
    {
        const string ENTRY_COLLECTION_URI = "http://livedoor.blogcms.jp/atom/blog/{0}/article";
        const string MEDIA_COLLECTION_URI = "http://livedoor.blogcms.jp/atom/blog/{0}/image";
        
        static void Main(string[] args)
        {
            try
            {
                // 引数のチェックは省略
                // 第一引数:username、正確にはルートエンドポイントの最後の部分やけどまぁログインIDと同じになると思われ
                // 第二引数:ブログ管理画面のAPIKEY画面で発行したパスワード
                // 第三引数:ローカルの画像ファイルのパス
                string userName = args[0];
                string apiKey = args[1];
                string filePath = args[2];

                string title = "記事タイトル";
                string cat = "カテゴリ名";
                string body = "記事本文";
                string more = "記事追記";

                string imgUrl = MediaUp(userName, apiKey, filePath);

                body += "<img src=\"" + imgUrl + "\">";

                BlogAdd(userName, apiKey, title, cat, body, more);
                
                
            }
            catch (Exception ex)
            {
                // 例外時にメッセージとスタックトレースを表示
                if (ex.InnerException != null)
                    Console.WriteLine(ex.InnerException.Message);
                else
                    Console.WriteLine(ex.Message);

                Console.WriteLine(ex.StackTrace);
            }
        }

        /// <summary>
        /// 記事投稿
        /// </summary>
        /// <param name="username"></param>
        /// <param name="apikey"></param>
        /// <param name="title"></param>
        /// <param name="cat"></param>
        /// <param name="body"></param>
        /// <param name="more"></param>
        /// <returns></returns>
        static bool BlogAdd(string username, string apikey, string title, string cat, string body, string more)
        {

            string reqStr = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
            reqStr += "<entry xmlns=\"http://www.w3.org/2005/Atom\"";
            reqStr += "    xmlns:app=\"http://www.w3.org/2007/app\"";
            reqStr += "    xmlns:blogcms=\"http://blogcms.jp/-/spec/atompub/1.0/\">";
            reqStr += "    <title>{0}</title>";
            reqStr += "    <category term=\"{1}\" />";
            reqStr += "    <blogcms:source>";
            reqStr += "        <blogcms:body><![CDATA[<p>{2}</p>]]></blogcms:body>";
            reqStr += "        <blogcms:more><![CDATA[<p>{3}</p>]]></blogcms:more>";
            reqStr += "    </blogcms:source>";
            reqStr += "    <app:control>";
            reqStr += "        <app:draft>no</app:draft>";
            reqStr += "    </app:control>";
            reqStr += "</entry>";

            reqStr = HttpUtility.HtmlDecode(string.Format(reqStr, title, cat, body, more));
            byte[] byteData = Encoding.UTF8.GetBytes(reqStr);

            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(string.Format(ENTRY_COLLECTION_URI, username));
            req.Headers.Add("X-WSSE", WsseCreate(username, apikey));
            req.Method = "POST";
            req.ContentType = "text/xml";
            req.ServicePoint.Expect100Continue = false;
            req.ContentLength = byteData.Length;

            Stream reqStream = req.GetRequestStream();
            reqStream.Write(byteData, 0, byteData.Length);
            reqStream.Close();

            // 送信のレスポンスを受け取る
            WebResponse webRes = req.GetResponse();
            using (XmlTextReader XmlReader = new XmlTextReader(webRes.GetResponseStream()))
            {
                XmlReader.WhitespaceHandling = WhitespaceHandling.None;
                while (XmlReader.Read())
                {

                    if (XmlReader.NodeType == XmlNodeType.Element)
                    {
                        if (XmlReader.Name == "fault")
                        {
                            return false;
                        }

                    }
                }
            }
            return true;
        }

        /// <summary>
        /// 画像アップロード
        /// </summary>
        /// <param name="username"></param>
        /// <param name="apikey"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        static string MediaUp(string username, string apikey, string filePath)
        {
            string ret = "";

            byte[] imgBytes;

            using (FileStream fs = new FileStream(filePath, FileMode.Open))
            using (Bitmap bmpData = new Bitmap(Image.FromStream(fs)))
            using (MemoryStream memStm = new MemoryStream())
            {
                // 保存フォーマットは任意
                bmpData.Save(memStm, System.Drawing.Imaging.ImageFormat.Jpeg);

                imgBytes = memStm.ToArray();

                memStm.Close();
                fs.Close();
            }

            string resText = "";

            using (WebClient wc = new WebClient())
            {
                var uri = new Uri(string.Format(MEDIA_COLLECTION_URI, username));
                var servicePoint = ServicePointManager.FindServicePoint(uri);
                servicePoint.Expect100Continue = false;
                wc.Headers.Add("X-WSSE", WsseCreate(username, apikey));
                //ヘッダにContent-Typeを加える
                wc.Headers.Add("Content-Type", "image/jpeg");

                //データを送信し、また受信する
                byte[] resData = wc.UploadData(uri, imgBytes);

                //受信したデータを表示する
                resText = Encoding.UTF8.GetString(resData);
            }

            XmlDocument xmldoc = new XmlDocument();
            xmldoc.Load(new StringReader(resText));
            XmlNode entry = xmldoc.LastChild;
            if (entry != null)
            {
                foreach (XmlNode entChild in entry.ChildNodes)
                {
                    if (entChild.Name == "content")
                    {
                        ret = entChild.Attributes["src"].Value;
                        break;
                    }

                }

            }

            return ret;
        }

        /// <summary>
        /// WSSE認証用の文字列作成
        /// </summary>
        /// <param name="username"></param>
        /// <param name="apikey"></param>
        /// <returns></returns>
        static string WsseCreate(string username, string apikey)
        {

            // nonce security token
            byte[] nonce = MakeRandomBytes(40);
            // created ISO-8601 formatting
            string created = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");

            // password sha1 digest (nonce+created+password)
            List<Byte> lstByte = new List<byte>();

            lstByte.AddRange(nonce);
            lstByte.AddRange(System.Text.Encoding.UTF8.GetBytes(created));
            lstByte.AddRange(System.Text.Encoding.UTF8.GetBytes(apikey));

            SHA1Managed sha1 = new SHA1Managed();

            byte[] passwordDigest = sha1.ComputeHash(lstByte.ToArray());

            // Build WSSE Header
            string format = "UsernameToken Username=\"{0}\", PasswordDigest=\"{1}\", Nonce=\"{2}\", Created=\"{3}\"";
            return String.Format(format,
                                 username,
                                 Convert.ToBase64String(passwordDigest),
                                 Convert.ToBase64String(nonce), created);


        }

        /// <summary>
        /// ランダムなバイト配列作成(WSSE認証用)
        /// </summary>
        /// <param name="makebytes"></param>
        /// <returns></returns>
        static byte[] MakeRandomBytes(int makebytes)
        {
            RNGCryptoServiceProvider rnd = new RNGCryptoServiceProvider();

            Byte[] rndBuffer = new Byte[makebytes];
            rnd.GetBytes(rndBuffer);

            return rndBuffer;
        }

    }
}

 
WSSE認証の部分は以下の記事を参考にしました、ありがとうございます。
tekkの日記 C#,VB.NET