本記事の対象APIは既にサポートされていません。記事は参考程度にご利用ください。
はじめに
前回に続いてWindows Live Photo APIについてです。今回は Photo APIを利用して実際にLiveサービスへコードからアクセスしてみましょう。図1のようなAPIの動作を確認できるページを作成します。Liveスペースのフォトの表示が可能です。

Live Photo APIの利用は、
Webサイトの作成
はじめにWeb Developerから新規にWebサイトを作成します

作成するWebサイトは、
<form id="form1" runat="server">
<div>
<asp:HyperLink ID="ConsentHyperLink" runat="server" Visible="False">Request Consent</asp:HyperLink>
</div>
<asp:Panel ID="ResoucePanel" runat="server">
<div>
<asp:ListBox ID="AlbumListBox" runat="server" Width="180px" Height="200px" AutoPostBack="True"></asp:ListBox>
<asp:ListBox ID="PhotoListBox" runat="server" Width="180px" Height="200px" AutoPostBack="True"></asp:ListBox>
<asp:ListBox ID="ImageListBox" runat="server" Width="180px" Height="200px" AutoPostBack="True"></asp:ListBox>
</div>
<div>
<asp:Image ID="StreamImage" runat="server" Visible="false" />
</div>
</asp:Panel>
</form>

ListBoxはアルバム、
Live委任認証
最初にページ初期化処理を記述します。主にLive委任認証の部分になります。委任認証については本連載第14回・
Photo APIを利用するには、
以下にページの初期化処理部分のコードを示します。Default.
' Imports System.Xml
Protected NamespaceManager As XmlNamespaceManager
Protected DelegateionToken As String = ""
Protected LocationId As String = ""
Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
NamespaceManager = New XmlNamespaceManager(New NameTable)
NamespaceManager.AddNamespace("atom", "http://www.w3.org/2005/Atom")
'NamespaceManager.AddNamespace("metadata", "http://schemas.microsoft.com/ado/2007/08/dataservices/metadata")
'NamespaceManager.AddNamespace("photos", "http://dev.live.com/photos")
If Request.Cookies("delt") IsNot Nothing AndAlso Request.Cookies("delt").Value <> "" AndAlso _
Request.Cookies("lid") IsNot Nothing AndAlso Request.Cookies("lid").Value <> "" Then
' Cookie に委任トークン、Location ID(lid)が格納されている場合
Me.DelegateionToken = Request.Cookies("delt").Value
Me.LocationId = Request.Cookies("lid").Value
' アルバム一覧表示
ListAlbums()
ElseIf Request.Form("ConsentToken") <> "" Then
' POSTデータに承認トークンが格納されている場合
' 承認トークンから各パラメータ取得
Dim consentToken As String = Request.Form("ConsentToken")
Dim pairs As Specialized.NameValueCollection = Parse(HttpUtility.UrlDecode(consentToken))
' 暗号化された承認トークンの場合、複合化
If pairs("eact") <> "" Then
pairs = Parse(HttpUtility.UrlDecode(DecryptToken(pairs("eact"), "*** Secret key ***")))
End If
' delt(委任トークン)とlidパラメータ値を Cookie に保存
Dim expires As DateTime = New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
expires = expires.AddSeconds(CUInt(pairs("exp"))).ToLocalTime()
If pairs("delt") <> "" Then
Response.Cookies("delt").Value = pairs("delt")
Response.Cookies("delt").Expires = expires
Response.Cookies("lid").Value = pairs("lid")
Response.Cookies("lid").Expires = expires
End If
Me.DelegateionToken = Request.Cookies("delt").Value
Me.LocationId = Request.Cookies("lid").Value
' アルバム一覧表示
ListAlbums()
Else
' 上記以外の場合、承認要求ページへリンクを表示
Dim ru As String = HttpUtility.UrlEncode("http://***/")
Dim pl As String = HttpUtility.UrlEncode("http://***/policy.html")
Dim ps As String = "SpacesPhotos.Read,SpacesPhotos.ReadWrite"
Dim mkt As String = "ja-JP"
ConsentHyperLink.NavigateUrl = String.Format("https://consent.live.com/Delegation.aspx?ru={0}&ps={1}&pl={2}&mkt={3}", ru, ps, pl, mkt)
ConsentHyperLink.Visible = True
ResoucePanel.Visible = False
End If
End Sub
ページを表示する際に、
また、
上記のコードでは、
リソースのアクセス
ATOMプロトコルによるPhoto APIでは、
https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos[/リソースへのパス]
アクセスする際にはAuthorizationヘッダを使い、
今回作成するWebサイトでは、
以上までの部分について、
Protected Function RetrieveResource(ByVal path As String) As XmlDocument
' リクエストの作成
Dim request As HttpWebRequest = DirectCast(WebRequest.Create(path), HttpWebRequest)
request.Method = "GET"
' Authorization ヘッダの指定
request.Headers(HttpRequestHeader.Authorization) = String.Format("DelegatedToken dt=""{0}""", Me.DelegateionToken)
Dim document As New XmlDocument
Try
' レスポンスの取得
Using response As HttpWebResponse = DirectCast(request.GetResponse, HttpWebResponse), _
reader As New System.IO.StreamReader(response.GetResponseStream, System.Text.Encoding.UTF8)
document.LoadXml(reader.ReadToEnd)
End Using
Return document
Catch webEx As WebException
document.InnerXml = "<exception>" & webEx.Message & "</exception>"
Return document
End Try
End Function
例外が発生した場合は、
アルバム一覧の取得
それではアルバムの一覧情報を取得してみましょう。アルバム一覧を表すリソースパスは
https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders
このURLへアクセスすると次のような<feed>要素からはじまるAtomプロトコルによるXMLが返ってきます。
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders</id>
<LivePhotos:SpaceUsed xmlns:LivePhotos="http://dev.live.com/photos">12345678</LivePhotos:SpaceUsed>
...
<entry LP:type="Folder">
...
</entry>
<entry LP:type="Folder">
...
</entry>
<entry LP:type="Folder">
...
</entry>
</feed>
多くの部分を省略していますが、
<entry LP:type="Folder" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<id>https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)</id>
...
<title>Album Name</title>
<category scheme="http://dev.live.com/photos/scheme" term="Folder" label="Folder" />
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)" rel="edit" type="application/atom+xml;type=entry" title="Folder" />
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos" rel="related" type="application/atom+xml;type=feed" title="Photos" />
<content type="application/xml">
<LP:properties>
<LivePhotos:ID xmlns:LivePhotos="http://dev.live.com/photos">123</LivePhotos:ID>
</LP:properties>
</content>
</entry>
ユーザーが付けたアルバム名は<title>要素の値からわかります。APIにより付けられたアルバムのIDやこのアルバムのリソースパスは、
以上から、
Protected Sub ListAlbums()
AlbumListBox.Items.Clear()
PhotoListBox.Items.Clear()
ImageListBox.Items.Clear()
StreamImage.Visible = False
End Sub
上記UIの処理に続いて、
Dim document As XmlDocument = _
RetrieveResource("https://cumulus.services.live.com/@C@" & Me.LocationId & "/AtomSpacesPhotos/Folders")
If document.SelectNodes("/exception").Count > 0 Then
Exit Sub
End If
lidパラメータを指定する部分には、
今回のアプリケーションではAtomプロトコル用のXML名前空間しか使用しませんが、
実際にXML文書から<title>要素の値を取得するコードは次のようになります。
Dim nodeList As XmlNodeList = document.SelectNodes("/atom:feed/atom:entry", NamespaceManager)
For Each node As XmlNode In nodeList
' アルバム名 取得
Dim name As String = node.SelectSingleNode("atom:title", NamespaceManager).FirstChild.Value
' アルバム内のフォトのリソースパス 取得
Dim url As String = node.SelectSingleNode("atom:link[@rel='related']", NamespaceManager).Attributes("href").Value
' リストに追加
AlbumListBox.Items.Add(New ListItem(name, url))
Next
For Eachによりすべての<feed>.<entry>要素から<title>要素の値を取得しています。上記コードでは、
以上がListAlbumsメソッドになります。このメソッドの呼び出しは、
フォト一覧の取得
次は、
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos</id>
<title>Album Name</title>
...
<entry LP:type="Photo">
...
</entry>
<entry LP:type="Photo">
...
</entry>
<entry LP:type="Photo">
...
</entry>
</entry>
アルバム内のフォトの数だけ<entry>要素があります。フォトを表す<etnry>要素は次のようになっています。
<entry LP:type="Photo" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<id>https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)</id>
<title>Photo Name</title>
...
<summary type="html"><img src="http://blufiles.storage.live.com/xxxxxxxx" border="0" alt="Photo Name" /></summary>
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)" rel="edit" type="application/atom+xml;type=entry" title="Photo" />
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/$value" rel="edit-media" type="image/jpeg" title="ImageStream" />
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/ImageStreams" rel="related" type="application/atom+xml;type=feed" title="ImageStreams" />
<content type="image/jpeg" src="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/$value" length="3000" />
<LP:properties>
<LivePhotos:ID xmlns:LivePhotos="http://dev.live.com/photos">456</LivePhotos:ID>
<LivePhotos:Version xmlns:LivePhotos="http://dev.live.com/photos">1</LivePhotos:Version>
<LivePhotos:Name xmlns:LivePhotos="http://dev.live.com/photos">Photo Name</LivePhotos:Name>
</LP:properties>
</entry>
少し省略してありますが、
今回のアプリケーションでは、
Protected Sub ListPhotos(ByVal path As String)
PhotoListBox.Items.Clear()
ImageListBox.Items.Clear()
StreamImage.Visible = False
Dim document As XmlDocument = RetrieveResource(path)
If document.SelectNodes("/exception").Count > 0 Then
Exit Sub
End If
Dim nodeList As XmlNodeList = document.SelectNodes("/atom:feed/atom:entry", NamespaceManager)
For Each node As XmlNode In nodeList
' フォト名 取得
Dim name As String = node.SelectSingleNode("atom:title", NamespaceManager).FirstChild.Value
' フォトのタイプのリソースパス 取得
Dim url As String = node.SelectSingleNode("atom:link[@rel='related']", NamespaceManager).Attributes("href").Value
' リストに追加
PhotoListBox.Items.Add(New ListItem(name, url))
Next
End Sub
メソッド名をListPhotosとして、
このListPhotosメソッドを呼び出すコードを追加します。アルバムのリストボックスの項目が選択された場合に呼び出すようにしましょう。AlbumListBoxのSelectedIndexChangedイベント処理を次のように記述します。
Protected Sub AlbumListBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles AlbumListBox.SelectedIndexChanged
Dim item As ListItem = AlbumListBox.SelectedItem
If item IsNot Nothing Then
ListPhotos(item.Value)
End If
End Sub
リストボックスの項目が選択されていた場合、
以上で、
フォトのタイプ一覧の取得
Photo APIで扱うストレージ構造は、
リソースパスは
<entry LP:type="ImageStream" xmlns:LP="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
<id>https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/ImageStreams(2)</id>
<title>Thumbnail</title>
...
<content type="image/jpeg" src="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/ImageStreams(2)/$value" length="3000" />
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/ImageStreams(2)/$value" rel="edit-media" type="image/jpeg" title="ImageStream" />
<link href="https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos/Folders(123)/Photos(456)/ImageStreams(2)" rel="edit" type="application/atom+xml;type=entry" title="ImageStream" />
<LP:properties>
<LivePhotos:ID xmlns:LivePhotos="http://dev.live.com/photos">1234</LivePhotos:ID>
<LivePhotos:PreAuthURL xmlns:LivePhotos="http://dev.live.com/photos">http://blufiles.storage.live.com/xxxxxxxx</LivePhotos:PreAuthURL>
<LivePhotos:SizeX xmlns:LivePhotos="http://dev.live.com/photos">96</LivePhotos:SizeX>
<LivePhotos:SizeY xmlns:LivePhotos="http://dev.live.com/photos">96</LivePhotos:SizeY>
</LP:properties>
</entry>
この例ではサムネイル用のフォトを<entry>要素を表しています。<content>、
コードを以下に示します。ListImagesというメソッドにしました。
Protected Sub ListImages(ByVal path As String)
ImageListBox.Items.Clear()
StreamImage.Visible = False
Dim document As XmlDocument = RetrieveResource(path)
If document.SelectNodes("/exception").Count > 0 Then
Exit Sub
End If
Dim nodeList As XmlNodeList = document.SelectNodes("/atom:feed/atom:entry", NamespaceManager)
For Each node As XmlNode In nodeList
' フォトのタイプ 取得
Dim name As String = node.SelectSingleNode("atom:title", NamespaceManager).FirstChild.Value
' フォトのバイナリデータのリソースパス 取得
Dim url As String = node.SelectSingleNode("atom:link[@rel='edit-media']", NamespaceManager).Attributes("href").Value
' リストに追加
ImageListBox.Items.Add(New ListItem(name, url))
Next
End Sub
フォト一覧のリストボックスの項目を選択したときに、
Protected Sub PhotoListBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles PhotoListBox.SelectedIndexChanged
Dim item As ListItem = PhotoListBox.SelectedItem
If item IsNot Nothing Then
ListImages(item.Value)
End If
End Sub
これまでと同じですね。以上で、
フォトバイナリデータの取得
最後にフォト自身のバイナリデータを取得するコードを書きます。バイナリデータのリソースパスは、
ソリューションエクスプローラのWebサイトのURLを右クリックし、

ここでは
ImageHandler.
IHttpHandlerインターフェースは、
- ProcessRequestメソッド
- IsReusableプロパティ
を持っています。これらのメンバーを実装します。
ProcessRequestメソッドには、
今回のアプリケーションでは、
まず、
Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest
' delt・url パラメータ取得
Dim delt As String = context.Request.Cookies("delt").Value
Dim url As String = context.Request.QueryString("url")
' パラメータが空文字の場合、Bad Request として返す
If delt = "" OrElse url = "" Then
context.Response.StatusCode = HttpStatusCode.BadRequest
Exit Sub
End If
' リクエストの作成
Dim request As HttpWebRequest = DirectCast(WebRequest.Create(url), HttpWebRequest)
request.Method = "GET"
request.Headers(HttpRequestHeader.Authorization) = String.Format("DelegatedToken dt=""{0}""", delt)
' (この後にさらに応答用の処理を追記する)
End Sub
CookieおよびURLに指定されたパラメータは、
上記のコードに、
Try
' レスポンスの取得
Dim response As HttpWebResponse = DirectCast(request.GetResponse, HttpWebResponse)
context.Response.ContentType = response.ContentType
' バイナリデータの取得、およびレスポンス用のストリームに書き込み
Using stream As System.IO.Stream = response.GetResponseStream
Dim buf As Integer = stream.ReadByte
Do While buf <> -1
context.Response.OutputStream.WriteByte(CByte(buf))
buf = stream.ReadByte
Loop
End Using
Catch ex As Exception
context.Response.StatusCode = HttpStatusCode.InternalServerError
End Try
このファイルが返すレスポンス情報はHttpContext.
IsReusableプロパティは、
Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
以上で画像を返すページができました。
Default.
Protected Sub ImageListBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles ImageListBox.SelectedIndexChanged
Dim item As ListItem = ImageListBox.SelectedItem
If item IsNot Nothing Then
StreamImage.ImageUrl = "ImageHandler.ashx?url=" & HttpUtility.UrlEncode(item.Value)
StreamImage.Visible = True
End If
End Sub
ImageListBoxのSelectedIndexChangedイベント処理を上記のように書きます。選択された項目の値を使ってImageコントロールのImageUrlを指定し、
いかがでしたでしょうか。今回作成するWebアプリケーションは以上で完成です。実行して動作を確認してみましょう。Live IDアカウントによるサインイン後、