使ってみよう! Windows Live SDK/API

第21回Windows Live Photo API(3)

本記事の対象APIは既にサポートされていません。記事は参考程度にご利用ください。

アルバムとフォトの編集

前回はWindows Live Photo APIを利用した簡単なWebアプリケーションを作成しました。そのアプリケーションでは以下の読み取りに関する内容を実装しました。

  • アルバム・フォト・フォトタイプの一覧取得
  • フォトバイナリデータ取得

今回は、作成や編集など、以下の操作ができるようアプリケーションを拡張します。

  • アルバムの作成
  • フォトのアップロード
  • アルバム・フォトの名前(タイトル)変更
  • アルバム・フォトの削除

作成するWebアプリケーションを図1に示します。

図1 作成するWebアプリケーション
図1 作成するWebアプリケーション

あまり良いUIではありませんが、APIの確認には十分でしょう。前回に作成したものに機能を追加する形で紹介します。

リクエストとレスポンス

アルバムおよびフォト情報の取得やフォトのダウンロード時には、すべてHTTP GETメソッドを使用していました。対して、アルバム作成やアップロード、名前変更、削除ではリクエスト時にPOST、PUT、DELETEといったメソッドを使います。

最初に今回のコードで共通で利用するメソッドを作成しておきます。Default.aspx.vbファイルに追記してください。アクセスするURLとHTTPメソッドを指定するとHttpWebRequestオブジェクトを返すメソッドをまず用意します。

Protected Function CreatedRequest(ByVal path As String, Optional ByVal method As String = "GET") As HttpWebRequest
    Dim request As HttpWebRequest = DirectCast(WebRequest.Create(path), HttpWebRequest)
    request.Method = method
    request.Headers(HttpRequestHeader.Authorization) = String.Format("DelegatedToken dt=""{0}""", Me.DelegateionToken)
    Return request
End Function

Live委任認証用のヘッダもここで追加しています。

続いてLiveサービスからのレスポンスを取得する部分もメソッドとして作成しておきましょう。

前回はXML文書やバイナリデータを取得して何かしらの処理をしていましたが、今回は基本的にHTTPステータスコードのみ、または それに加えてレスポンスに含まれるヘッダ内容のみを必要とします。

ここではレスポンス取得するメソッドを次のように作成しました。

Protected Function GetResponse(ByVal request As HttpWebRequest) As HttpStatusCode
    Try
        Using response As HttpWebResponse = DirectCast(request.GetResponse, HttpWebResponse)
            Return response.StatusCode
        End Using
    Catch webEx As WebException
        Return DirectCast(webEx.Response, HttpWebResponse).StatusCode
    End Try
End Function

HttpWebRequestオブジェクトを引数として渡し、HttpStatusCodeが返ります。

アルバム作成およびフォトのアップロードに成功した場合、アップロードにより作成されたリソースのパスを示す文字列がレスポンスのLocationヘッダに格納されています。これから作成するアプリケーションではLocationで指定された値を使用していないのですが、メソッドは次のように作り呼び出しています。

Protected Function GetResponse(ByVal request As HttpWebRequest, ByRef location As String) As HttpStatusCode
    Try
        location = ""
        Using response As HttpWebResponse = DirectCast(request.GetResponse, HttpWebResponse)
            location = response.Headers(HttpResponseHeader.Location)
            Return response.StatusCode
        End Using
    Catch webEx As WebException
        Return DirectCast(webEx.Response, HttpWebResponse).StatusCode
    End Try
End Function

参照渡しによりLocationヘッダの値を返すようにしています。

アルバムの作成

最初に新しいアルバムを作成してみましょう。アルバム作成時に指定するリソースパスは、アルバムのコレクションを表すパス、つまり「/Folders」です。URL全体では次のようになります。

https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders

そしてPOSTメソッドにより以下のXML文書を送信します。

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:LivePhotos="http://dev.live.com/photos" LP:type="Folder">
   <category scheme="http://dev.live.com/AppStorage/scheme" term="Folder" label="Folder" />
   <title>[アルバム名]</title>
</entry>

Atomプロトコルの<entry>要素になっています。<title>要素に新しく作成するアルバム名を指定しています。

以上のリクエストを行うと、作成に成功した場合、HTTPステータスコード201(Created)が返ってきます。また、作成されたアルバムのリソースパスはレスポンスに含まれるLocationヘッダによりわかります。

ここまでをアプリケーションに実装します。まず、アルバム名を指定するテキストボックスと作成ボタンをWebページに追加します。図1では、以下のコードをDefault.aspxの<asp:Panel>タグ内に追加しています。

<fieldset>
<legend>アルバムの作成</legend>
    <label>アルバム名: </label>
    <asp:TextBox ID="NewAlbumNameTextBox" runat="server"></asp:TextBox>
    <asp:Button ID="CreateButton" runat="server" Text="作成" />
</fieldset>

作成ボタンがクリックされたときのコードは、先ほど作成したメソッドと組み合わせて、Clickイベント処理を次のように記述します。

Protected Sub CreateButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles CreateButton.Click
    ' リクエストの作成
    Dim request As HttpWebRequest = CreatedRequest("https://cumulus.services.live.com/@C@" & Me.LocationId & "/AtomSpacesPhotos/Folders", "POST")

    ' <entry>要素の作成
    Dim entry As String = _
        "<entry xmlns=""http://www.w3.org/2005/Atom"" xmlns:LP=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" xmlns:LivePhotos=""http://dev.live.com/photos"" LP:type=""Folder"">" & _
        "<category scheme=""http://dev.live.com/AppStorage/scheme"" term=""Folder"" label=""Folder"" />" & _
        "<title>" & HttpUtility.HtmlEncode(NewAlbumNameTextBox.Text) & "</title>" & _
        "</entry>"

    ' リクエストの各種設定
    Dim buffer() As Byte = System.Text.Encoding.UTF8.GetBytes(entry)
    request.ContentType = "application/atom+xml"
    request.ContentLength = buffer.Length

    Dim stream As System.IO.Stream = request.GetRequestStream
    stream.Write(buffer, 0, buffer.Length)

    ' レスポンスの取得
    Dim location As String = "" ' 作成されたアルバムのリソースパス URL
    Dim status As HttpStatusCode = GetResponse(request, location)
    If status = HttpStatusCode.Created Then
        ListAlbums() ' アルバム一覧の表示 (前回のコード)
    Else
        ' (エラー処理)
    End If
End Sub

CreateRequestメソッドを呼び出しリクエストを作成した後、<entry>要素をバイト配列としてStreamへ書き込みやContentTypeの指定を行っています。

レスポンスの取得では、成功した場合に前回作成したメソッドを呼び出しアルバム一覧の表示を更新しています。上記コードではLocationヘッダの値をlocation変数に格納するようにしていますが、使用はしていません。作成に失敗した場合も処理を省略しています。実際には、ユーザーに失敗したこと、またその理由を通知するようにするとよいでしょう。

フォトのアップロード

フォトのアップロードは、アルバムの作成と同じPOSTメソッドを使用します。また成功した場合、Locationヘッダによりフォトのリソースパスを取得できる点で同じです。ただし、送信するデータはXML文書ではなく画像ファイルのバイナリデータとなります。

リソースパスには、フォトを追加したいアルバムのフォト一覧を示すパスを指定します。⁠/Folders(n)/Photos」といったパスでしたね。アルバム自身を表すパスではなくフォトのコレクションを表すパスということに注意してください。

このほか、ContentTypeヘッダにアップロードする画像ファイルのContent Type(image/jpegなど)を指定し、フォトの名前をslugヘッダに指定します。

それでは処理を実装しましょう。Webページには、FileUploadコントロール、フォト名を指定するTextBoxとアップロードを実行するためのButtonを配置します。

<fieldset>
<legend>フォトのアップロード</legend>
    <label>フォト名: </label>
    <asp:TextBox ID="NewPhotoNameTextBox" runat="server"></asp:TextBox>
    <br />
    <label>ファイル: </label>
    <asp:FileUpload ID="PhotoFileUpload" runat="server" />
    <asp:Button ID="UploadButton" runat="server" Text="アップロード" />
</fieldset>

アップロードボタンをクリックしたときの処理は次のようになります。

Protected Sub UploadButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles UploadButton.Click
    ' アルバムリストボックスから選択されている ListItem 取得
    Dim item As ListItem = AlbumListBox.SelectedItem
    If item Is Nothing Then
        Exit Sub ' 選択されていない場合は処理を終了
    End If

    ' リクエストの作成 (ListItem.Value には、フォト一覧のリソースパスが格納されている)
    Dim request As HttpWebRequest = CreatedRequest(item.Value, "POST")

    ' アルバム名を指定
    request.Headers.Add("slug", HttpUtility.UrlEncode(NewPhotoNameTextBox.Text))

    ' ContentType を指定
    request.ContentType = PhotoFileUpload.PostedFile.ContentType.Replace("/pjpeg", "/jpeg")

    ' バイナリデータの指定
    Dim buffer() As Byte = PhotoFileUpload.FileBytes
    request.ContentLength = buffer.Length
    Dim stream As System.IO.Stream = request.GetRequestStream
    stream.Write(buffer, 0, buffer.Length)

    ' レスポンスの取得
    Dim location ADims String = "" ' アップロードしたフォトのリソースパス URL
    Dim status As HttpStatusCode = GetResponse(request, location)
    If status = HttpStatusCode.Created Then
        ListPhotos(item.Value) ' フォト一覧の表示 (前回のコード)
    Else
        ' (エラー処理)
    End If
End Sub

フォトを追加する先のアルバムの選択は、前回に作成済みのアルバム一覧のリストボックスを使用しています。ContentTypeにJPEG形式のimage/pjpegを指定した場合、サポートしていない画像形式と応答されアルバムに追加できませんが、pjpeg部分をjpegとするだけで追加できますので、上記コードは置換で対応しています。

APIの制限・仕様

ここまででアルバムの作成とフォトのアップロードができるようになりました。実際に実行して確認してみましょう図2⁠。

図2 実行結果
図2 実行結果

実行して試す際の留意事項およびAPIの制限と思われる点をここで述べておきます。

日本語・特殊文字

アルバム名・フォト名に日本語や特殊文字を使用した場合、Bad Requestとして応答されるか、文字化けした名前となってしまうようです。Live Spacesのフォト自体は日本語に対応していますのでAPIの制限と思われます。

アルバムのアクセス制限

Live Spacesからアルバムを編集すると、インターネット上の全員・特定のユーザー・自分のみの3種類のアクセス権限を指定できます。APIにより作成した場合は、インターネット上の全員がアクセスできる状態で作成されます。

フォトタイプ

フォトにはサムネイル用やWeb用といったタイプがあり、ひとつのフォトには複数のタイプの異なるバイナリデータを持つ構造となっています。APIでアップロードした場合、大きい画像は自動で縮小され、WebReadyおよびThumbnailのタイプが作成されるようです。

アルバム・フォトの名前変更

アルバムとフォトの名前(タイトル)を変更してみましょう。名前の変更は、PUTメソッドを使用し<entry>要素から成るXML文書を送信して行います。成功した場合に応答されるHTTPステータスコードは、204(No Content)です。

指定するリソースパスと送信する<entry>要素は、アルバムとフォトで異なります。順に確認した後、コードを記述します。

アルバム名の変更

アルバム名を変更する際に指定するリソースパスは、アルバム単体を表す「/Folders(n)」といったパスになります。

<entry>要素は次のようになります。これはアルバム作成時とまったく同じです。

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:LivePhotos="http://dev.live.com/photos" LP:type="Folder">
   <category scheme="http://dev.live.com/AppStorage/scheme" term="Folder" label="Folder" />
   <title>[アルバム名]</title>
</entry>

フォト名の変更

フォト名を変更する際に指定するリソースパスは、フォト単体を表す「/Folders(n)/Photos(m)」といったパスになります。

<entry>要素は次のようになります。アルバムのときと比べて属性値が異なっています。

<entry xmlns="http://www.w3.org/2005/Atom" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata" xmlns:LivePhotos="http://dev.live.com/photos" xmlns:Live="LiveAtomBase:" LP:type="Photo">
   <category scheme="http://dev.live.com/photos/scheme" term="Photo" label="Photo" />
   <title>[フォト名]</title>
</entry>

コードの記述

それでは名前変更の処理を記述しましょう。Webページには、名前入力用のテキストボックス(アルバムとフォトで共有⁠⁠、名前変更用のボタン(アルバムとフォトで個別)を配置しているものとします。また、変更する対象の選択は前回作成済みのリストボックスから行うものとします。

<fieldset>
<legend>選択されているアルバムまたはフォトの名前変更</legend>
    <label>新しい名前: </label>
    <asp:TextBox ID="NewNameTextBox" runat="server"></asp:TextBox>
    <asp:Button ID="AlbumRenameButton" runat="server" Text="アルバム名変更" />
    <asp:Button ID="PhotoRenameButton" runat="server" Text="フォト名変更" />
</fieldset>

アルバムの名前変更の処理を記述します。Clickイベントは次のように処理します。

Protected Sub AlbumRenameButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles AlbumRenameButton.Click
    ' アルバムリストボックスから選択されている ListItem 取得
    Dim item As ListItem = AlbumListBox.SelectedItem
    If item Is Nothing Then
        Exit Sub ' 選択されていない場合は処理を終了
    End If

    ' リクエストの作成
    Dim request As HttpWebRequest = CreatedRequest(item.Value.Replace("/Photos", ""), "PUT")

    ' <entry>要素の作成
    Dim entry As String = _
       "<entry xmlns=""http://www.w3.org/2005/Atom"" xmlns:LP=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" xmlns:LivePhotos=""http://dev.live.com/photos"" LP:type=""Folder"">" & _
       "<category scheme=""http://dev.live.com/AppStorage/scheme"" term=""Folder"" label=""Folder"" />" & _
       "<title>" & HttpUtility.HtmlEncode(NewNameTextBox.Text) & "</title>" & _
       "</entry>"

    ' リクエストの各種設定
    Dim buffer() As Byte = System.Text.Encoding.UTF8.GetBytes(entry)
    request.ContentType = "application/atom+xml"
    request.ContentLength = buffer.Length

    Dim stream As System.IO.Stream = request.GetRequestStream
    stream.Write(buffer, 0, buffer.Length)

    ' レスポンスの取得
    Dim status As HttpStatusCode = GetResponse(request)
    If status = HttpStatusCode.NoContent Then
        ListAlbums() ' アルバム一覧の表示 (前回のコード)
    Else
        ' (エラー処理)
    End If
End Sub

アルバムの選択はアルバム一覧のリストボックスを利用しています。これはフォトのアップロードの処理と同じです。ただし、ListItem.Valueプロパティにはフォト一覧を示すパスが設定されていましたので、パスから「/Photos」を削除してアルバムを示すパスにしてからメソッド呼び出しをしています。

レスポンスの取得では、HTTPステータスコードしか必要ありませんので最初に作成したLocationヘッダを返さないほうのGetResponseメソッドを呼んでいます。

続いてフォト名変更のコードは次のようになります。

Protected Sub PhotoRenameButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles PhotoRenameButton.Click
    ' フォトリストボックスから選択されている ListItem 取得
    Dim item As ListItem = PhotoListBox.SelectedItem
    If item Is Nothing Then
        Exit Sub ' 選択されていない場合は処理を終了
    End If

    ' リクエストの各種設定
    Dim request As HttpWebRequest = CreatedRequest(item.Value.Replace("/ImageStreams", ""), "PUT")

    ' <entry>要素の作成
    Dim entry As String = _
        "<entry xmlns=""http://www.w3.org/2005/Atom"" xmlns:LP=""http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"" xmlns:LivePhotos=""http://dev.live.com/photos"" xmlns:Live=""LiveAtomBase:"" LP:type=""Photo"">" & _
        "<category scheme=""http://dev.live.com/photos/scheme"" term=""Photo"" label=""Photo"" />" & _
        "<title>" & HttpUtility.HtmlEncode(NewNameTextBox.Text) & "</title>" & _
        "</entry>"

    ' リクエストの各種設定
    Dim buffer() As Byte = System.Text.Encoding.UTF8.GetBytes(entry)
    request.ContentType = "application/atom+xml"
    request.ContentLength = buffer.Length

    Dim stream As System.IO.Stream = request.GetRequestStream
    stream.Write(buffer, 0, buffer.Length)

    ' レスポンスの取得
    Dim status As HttpStatusCode = GetResponse(request)
    If status = HttpStatusCode.NoContent Then
        ' フォト一覧の表示 (前回のコード)
        ListPhotos(AlbumListBox.SelectedItem.Value)
    Else
        ' (エラー処理)
        Label1.Text = status.ToString
    End If
End Sub

アルバム名変更のときとほぼ同様ですが、フォト一覧のリストボックスを利用している点、<entry>要素、レスポンス取得後にフォト一覧を更新している点で異なっています。

名前の変更は、アルバムの作成・フォトのアップロード同様に使用できる文字に制限があります。

アルバム・フォトの削除

最後はアルバムとフォトの削除についてです。削除する場合は、DELETEメソッドを使い、リソースパスには名前変更のときと同じようにアルバム単体・フォト単体を示す値を指定します。成功したときのHTTPステータスコードは204(No Content)です。

ここまでを順に実装していれば、一番単純な処理になります。さっそくコードを記述します。Webページは以下のように、アルバムとフォト用の削除ボタンを配置しているものとします。

<fieldset>
<legend>選択されているアルバムまたはフォトの削除</legend>
    <asp:Button ID="AlbumDeleteButton" runat="server" Text="アルバムの削除" />
    <asp:Button ID="PhotoDeleteButton" runat="server" Text="フォトの削除" />
</fieldset>

アルバムの削除ボタンをクリックしたときの処理は次のように記述します。

Protected Sub AlbumDeleteButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles AlbumDeleteButton.Click
    ' アルバムリストボックスから選択されている ListItem 取得
    Dim item As ListItem = AlbumListBox.SelectedItem
    If item Is Nothing Then
        Exit Sub ' 選択されていない場合は処理を終了
    End If

    ' リクエストの作成
    Dim request As HttpWebRequest = CreatedRequest(item.Value.Replace("/Photos", ""), "DELETE")

    ' レスポンスの取得
    Dim status As HttpStatusCode = GetResponse(request)
    If status = HttpStatusCode.NoContent Then
        ListAlbums() ' アルバム一覧の表示 (前回のコード)
    Else
        ' (エラー処理)
    End If
End Sub

送信するバイナリデータがないためコードが短いです。パスの指定部分は名前変更のときと同じListItem.Valueプロパティからコレクション用のパス部分を削除する処理をしています。

フォトの削除ボタンをクリックしたときの処理は以下のようになります。

Protected Sub PhotoDeleteButton_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles PhotoDeleteButton.Click
    ' フォトリストボックスから選択されている ListItem 取得
    Dim item As ListItem = PhotoListBox.SelectedItem
    If item Is Nothing Then
        Exit Sub ' 選択されていない場合は処理を終了
    End If

    ' リクエストの作成
    Dim request As HttpWebRequest = CreatedRequest(item.Value.Replace("/ImageStreams", ""), "DELETE")

    ' レスポンスの取得
    Dim status As HttpStatusCode = GetResponse(request)
    If status = HttpStatusCode.NoContent Then
        ' フォト一覧の表示 (前回のコード)
        ListPhotos(AlbumListBox.SelectedItem.Value)
    Else
        ' (エラー処理)
    End If
End Sub

以上で今回作成するWebアプリケーションは完成です。

おわりに

今回でLive Photo ATOM APIについては一区切りとして終了です。いかがでしたでしょうか。以前に本連載で紹介したPhoto APIと同じLiveユーザーデータAPIに属するLive Application Based Storage APIと比較すると、Photo APIはLive Spaces上のフォトというエンドユーザーにもわかりやすい、エンドユーザー自身も管理している、データを操作できる点で異なっていたと思います(ユーザーデータを扱うAPIですので、Application Based Storageが少し特殊だったかもしれません。⁠⁠。少し変わったAPIだと思いますので、すぐにWebサイトで利用するということは難しいかもしれませんが、こういうAPIがあることを気に留めておくと、いずれ何かのタイミングでおもしろい利用が思いつくかもしれませんね。その際に本連載が役立てば幸いです。

おすすめ記事

記事・ニュース一覧