はてなフォトライフに画像をアップロードするプログラムをC#で実装してみた

自分のコスプレ写真を(ry

前回に引き続き、今回ははてなフォトライフに画像をアップロードするC#ライブラリをまとめます。
http://developer.hatena.ne.jp/ja/documents/fotolife/apis/atom

概要

はてなのユーザIDとパスワードを指定すると、はてなフォトライフに画像をアップロードできるライブラリ。
※前回のWSSE認証のヘッダを生成するライブラリが必要です。http://cpp.kanaharu.com/entry/2013/01/06/132031

HatenaPhotoLifeUploader.cs

using System;
using System.Text;
using System.IO;
using System.Net;

namespace Harunakasoft.Library
{
    public class HatenaPhotoLifeUploader
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="userName">はてなユーザ名</param>
        /// <param name="password">はてなパスワード</param>
        public HatenaPhotoLifeUploader(string userName, string password)
        {
            this.hatenaAccount = new HatenaAccount(userName, password);
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="hatenaAccount">はてなアカウント</param>
        public HatenaPhotoLifeUploader(HatenaAccount hatenaAccount)
        {
            this.hatenaAccount = hatenaAccount;
        }

        /// <summary>
        /// 画像をアップロードする
        /// </summary>
        /// <param name="path">画像パス</param>
        /// <param name="title">画像タイトル</param>
        /// <param name="folder">画像フォルダ</param>
        public void Upload(string path, string title, string folder = "")
        {
            byte[] data = GetImageBinaryData(path);
            Upload(data, title, folder);
        }

        /// <summary>
        /// 画像をアップロードする
        /// </summary>
        /// <param name="data">画像バイナリデータ</param>
        /// <param name="title">画像タイトル</param>
        /// <param name="folder">画像フォルダ</param>
        public void Upload(byte[] data, string title, string folder = "")
        {
            // アップロード用XML作成
            string uploadXML = MakeUpdateXML(data, title, folder);
            byte[] uploadBinary = Encoding.UTF8.GetBytes(uploadXML);

            // POST実行
            logger.Info("画像アップロード[POST]開始");
            logger.Info(" title  =" + title);
            logger.Info(" folder =" + folder);
            HttpWebRequest request;
            Stream stream;
            try
            {
                request = (HttpWebRequest)WebRequest.Create("http://f.hatena.ne.jp/atom/post");
                request.Headers.Add("X-WSSE", hatenaAccount.WSSEHeader);
                request.Method = "POST";
                stream = request.GetRequestStream();
                stream.Write(uploadBinary, 0, uploadBinary.Length);
                stream.Close();
            }
            catch (Exception e)
            {
                logger.Error("画像アップロード[POST]失敗 message=" + e.ToString());
                throw e;
            }
            logger.Info("画像アップロード[POST]終了");

            // レスポンス待ち
            logger.Info("画像レスポンス待ち開始");
            try
            {
                WebResponse response = request.GetResponse();
                Stream resStream = response.GetResponseStream();
                StreamReader reader = new StreamReader(resStream);
                string result = reader.ReadToEnd();
                resStream.Close();
                logger.Debug(result);
                reader.Close();
            }
            catch (Exception e)
            {
                logger.Error("画像レスポンス待ち失敗 message=" + e.ToString());
                throw e;
            }
            logger.Info("画像レスポンス待ち終了");
        }

        /// <summary>
        /// パスのファイルを開きバイナリデータを取得する
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        private byte[] GetImageBinaryData(string path)
        {
            if (path == null)
            {
                throw new ArgumentNullException("パスがnull");
            }
            if (!File.Exists(path))
            {
                throw new FileNotFoundException("指定されたパスにファイルが存在しない path=[{0}]", path);
            }

            logger.Info("画像ファイル読み込み開始");
            logger.Info(" path=" + path);
            byte[] data;
            try
            {
                FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read);
                long size = stream.Length;
                data = new byte[size];
                stream.Read(data, 0, (int)size);
                stream.Dispose();
            }
            catch (Exception e)
            {
                logger.Error("画像ファイル読み込み失敗 message=" + e.ToString());
                throw e;
            }
            logger.Info("画像ファイル読み込み終了 path=" + path);

            return data;
        }

        /// <summary>
        /// アップロード用XML作成
        /// </summary>
        /// <param name="data">画像バイナリデータ</param>
        /// <param name="title">画像タイトル</param>
        /// <param name="folder">画像フォルダ</param>
        /// <returns></returns>
        private string MakeUpdateXML(byte[] data, string title, string folder = "")
        {
            logger.Info("はてなフォトライフアップロード用XML作成開始");
            logger.Debug(" title  =" + title);
            logger.Debug(" folder =" + folder);
            logger.Debug(" datalen=" + data.Length);
            if (data == null)
            {
                throw new ArgumentNullException("データがnull");
            }
            if (data.Length <= 0)
            {
                throw new ArgumentException("データが空");
            }
            if (title == null)
            {
                throw new ArgumentNullException("タイトルがnull");
            }
            if (folder == null)
            {
                throw new ArgumentNullException("フォルダがnull");
            }

            StringBuilder buffer = new StringBuilder();
            buffer.Append("<entry xmlns=\"http://purl.org/atom/ns#\">\n");
            buffer.Append("  <title>" + title + "</title>");
            if (folder != null && folder.Length > 0)
            {
                buffer.Append("  <dc:subject>" + folder + "</dc:subject>\n");
            }
            buffer.Append("  <content mode=\"nonce64\" type=\"image/jpeg\">");
            buffer.Append(Convert.ToBase64String(data));
            buffer.Append("</content>\n");
            buffer.Append("</entry>");

            logger.Info("はてなフォトライフアップロード用XML作成終了");
#if DEBUG
            logger.Debug(buffer.ToString());
#endif
            return buffer.ToString();
        }

        private readonly HatenaAccount hatenaAccount;
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }
}

Uploadメソッドのfolder引数にフォルダ名を指定すると、同名のフォルダがすでにある場合は、そのフォルダに画像が配置。同名のフォルダがない場合は、新規作成した上で画像が配置されます。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Harunakasoft.Library;

namespace HatenaPhotoLifeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                HatenaPhotoLifeUploader uploader = new HatenaPhotoLifeUploader("name", "password");
                uploader.Upload(@"C:\Users\kanaharu\Desktop\reimu_twitter.jpg", "test");
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
    }
}