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

第20回Windows Live Photo API(2)

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

はじめに

前回に続いてWindows Live Photo APIについてです。今回は Photo APIを利用して実際にLiveサービスへコードからアクセスしてみましょう。図1のようなAPIの動作を確認できるページを作成します。Liveスペースのフォトの表示が可能です。

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

Live Photo APIの利用は、HTTPによるアクセスおよびXMLの解析などが主な処理ですので、言語は何を使用しても構いません。本連載では以前の回と同様に、開発ツールはVisual Web Developer 2008 Express Editionを、言語はVB.NETを使用します。

Webサイトの作成

はじめにWeb Developerから新規にWebサイトを作成します図2⁠。Live 委任認証を使用するため開発環境で実行するにはローカルIISを使用し、Windows Liveサービスから開発環境のPCにアクセスできるようにする必要があります。もしくは開発はローカルのPC上で、動作確認は実際にWebにアップロードして行います。

図2 新しいWebサイトの作成
図2 新しいWebサイトの作成

作成するWebサイトは、図1のように、リストボックスからアルバム→フォト→フォトのタイプの順に選択し、そのフォトを表示するという単純なものです。まずデザイン部分を書いてしまいましょう。Default.aspxのデザイン部分のコードは以下のようになります。図3も参考にページ上に各コントロールを配置してください。コードは<body>タグ内の部分を示しています。

<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>
図3 デザイナ画面
図3 デザイナ画面

ListBoxはアルバム、フォト、フォトのタイプ用の3個を配置し、リストの項目をクリックした時点で処理できるようAutoPostBackプロパティをTrueにしています。また、ListBoxとImage以外に委任認証用のHyperLinkも配置しています。

Live委任認証

最初にページ初期化処理を記述します。主にLive委任認証の部分になります。委任認証については本連載第14回15回で扱いましたので併せて参照してください。そのときに使用したコードをここでも使用しています。

Photo APIを利用するには、まず委任認証を利用してユーザーの承諾を得る必要がありました。作成するWebサイトでは、承認要求ページへのリンクを表示し、承認要求ページから返る承認トークンをCookieに保存します。保存する承認トークンのパラメータはdelt(委任トークン⁠⁠、lid(Location ID)パラメータのふたつです。

以下にページの初期化処理部分のコードを示します。Default.aspx.vbに記述します。

' 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

ページを表示する際に、Cookieにdelt・lidパラメータが格納されていない場合は、承認要求ページへのリンクを表示するようにしています。格納されていた場合は、リンクの代わりにリストボックスなどを表示します。

また、このページでは承認要求ページから戻ってきたときの処理も必要です。その部分がPOSTデータに承認トークンが格納されていた場合の処理になります。POSTデータからdelt・lidパラメータを取り出し、Cookieに保存しています。ParseメソッドやDecryptTokeメソッドについては本連載第14回15回で作成したメソッドです。

上記のコードでは、この後の説明に関する処理も含まれています。XML操作のためのXmlNamespaceManagerオブジェクトの生成部分とアルバム一覧を表示するListAlbumsメソッドです。delt・lidパラメータの値は別メソッドでも使用するのでProtected変数への設定もしています。

リソースのアクセス

ATOMプロトコルによるPhoto APIでは、前回に説明したように次の書式のURLへアクセスし、アルバムやフォト情報やバイナリデータを取得します。

https://cumulus.services.live.com/@C@[LID]/AtomSpacesPhotos[/リソースへのパス]

アクセスする際にはAuthorizationヘッダを使い、deltパラメータの値(委任トークン)を指定します。

今回作成するWebサイトでは、すべてHTTP GETメソッドによるアクセスになります。また、Liveサービスからの応答はフォトのバイナリデータを除き、XML文書となります。

以上までの部分について、リソースパスを指定するとXML文書を返すメソッドとして作成してみましょう。コードは次のようになります。

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

例外が発生した場合は、その内容を<exception>要素の値としたXmlDocumentオブジェクトを返すようにしています。

アルバム一覧の取得

それではアルバムの一覧情報を取得してみましょう。アルバム一覧を表すリソースパスは「/Folders」でした。アクセスURLは次のようになります。

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>

多くの部分を省略していますが、<feed>要素の中に、アルバムを表す<entry>要素がアルバムの数だけあります。<entry>要素は次のようになっています。

<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やこのアルバムのリソースパスは、<LivePhotos:ID>や<id>要素から取得できます。さらに、このアルバムが持つフォト一覧情報へのリソースパスは、rel属性の値が"related"の<link>要素の値からわかります。

以上から、アルバム一覧情報を取得して、ページ上のリストボックスにアルバム名を表示するコードを書いてみましょう。ここではListAlbumsという名前でメソッドを作っています。最初に各ListBoxの項目のクリアとImageの非表示処理を行います。

Protected Sub ListAlbums()
    AlbumListBox.Items.Clear()
    PhotoListBox.Items.Clear()
    ImageListBox.Items.Clear()
    StreamImage.Visible = False
End Sub

上記UIの処理に続いて、XMLを得る処理を書きます。

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パラメータを指定する部分には、ページ初期化処理で設定しておいたLocationId変数を使用しています。もし例外が起きた場合は、今回は何も処理せずメソッドを終わるようにしています。

続いてXMLを操作するコードを書きます。ここではXML操作にはXPathを使用しています。XML名前空間の管理にXmlNamespaceManagerクラスを使います。このオブジェクトはPage_Initメソッド内でインスタンス化していました。

今回のアプリケーションではAtomプロトコル用のXML名前空間しか使用しませんが、Photo APIには独自のXML名前空間が含まれています。それらを使用する場合はPage_Initメソッド内の該当部分のコメントを参考に、XML名前空間をXmlNamespaceManagerオブジェクトに追加してください。

実際に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>要素の値を取得しています。上記コードでは、<link rel="related">のhref属性値も取得しています。このふたつの値からListItemオブジェクトを作成し、ListBoxに追加します。これによりページではアルバム名を表示して、コードからはそのアルバム内のフォト一覧のリソースパスも取得が可能です。

以上がListAlbumsメソッドになります。このメソッドの呼び出しは、すでにページの初期化処理から行っています。ここまでを実行すると、承認要求ページへのリンクが表示され、リンクからLive IDアカウントでサインイン後に承諾すると、アルバム一覧が表示されると思います。

フォト一覧の取得

次は、ひとつのアルバム内にあるフォト一覧情報を取得してみましょう。フォト一覧を表すリソースパスは「/Folders(n)/Photos」のようになります。フォト一覧のXMLはアルバム一覧と同様に<feed>要素の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(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">&lt;img src="http://blufiles.storage.live.com/xxxxxxxx" border="0" alt="Photo Name" /&gt;</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>

少し省略してありますが、アルバムと同じように<title>要素からユーザーが付けたフォトの名前がわかります。<link>要素では、フォト自身を表すリソースパスのほか、バイナリデータを表すパス、さらに下の階層のフォトのタイプ一覧のパスがあります。ほかにも<summary>や<content>、<LP:properties>要素などからも情報が取得できます。

今回のアプリケーションでは、先ほどのアルバム一覧と同じく<title>要素と<link rel="related">要素を使用します。リストボックスにフォト一覧を表示するコードを書いてみましょう。内容はほぼアルバム一覧表示のときと同じです。コードは次のようになります。

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として、アルバム一覧と異なりアクセスするURLがアルバムによって異なるため引数でそのURLを受け取るようにしています。そのほかの処理は、UI処理と追加するリストボックスが異なるだけですね。

この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

リストボックスの項目が選択されていた場合、そのValueプロパティ(リソースへのURLを指定していました)を引数としてListPhotosメソッドを呼びます。

以上で、アルバムの選択からフォト一覧の表示までができました。

フォトのタイプ一覧の取得

Photo APIで扱うストレージ構造は、アルバム‐フォト‐フォトのタイプという3階層の構造でした。リストボックスの3個目は、3階層目であるWeb用やモバイル用などのフォトタイプ一覧を表示します。

リソースパスは「/Folders(n)/Photos(m)/ImageStreams」となります。取得できるXMLはこれまでと同じように<feed>要素からはじまりフォトの持つフォトタイプの数だけ<entry>要素が含まれています。その<entry>要素を以下に示します。

<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>、<LP:properties>からアルバムやフォトとは異なった情報が得られることがわかると思いますが、アプリケーションではこれまでと同様に<title>と<link rel="related">要素のみを使います。

コードを以下に示します。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

これまでと同じですね。以上で、リストボックス表示処理はすべて完了です。

フォトバイナリデータの取得

最後にフォト自身のバイナリデータを取得するコードを書きます。バイナリデータのリソースパスは、末尾に「/$value」を付けて表わします。今回のアプリケーションでは、カスタムHTTPハンドラという仕組みを使って、指定したフォトの画像を動的に返すページを作成します。

ソリューションエクスプローラのWebサイトのURLを右クリックし、メニューの「新しい項目の追加」をクリックし、開いたウィンドウから「ジェネリック ハンドラ」を追加します図4⁠。

図4 新しい項目の追加
図4 新しい項目の追加

ここでは「ImageHandler.ashx」という名前にしました。⁠http://***/ImageHandler.ashx?url=[リソースへのURL]のように このファイルへアクセスすると、urlパラメータに指定したフォトのバイナリデータを返すようにします。

ImageHandler.ashxには、IHttpHandlerインターフェースを実装したImageHandlerクラスが すでに書かれていると思います。このコードを編集して画像を返すようにします。

IHttpHandlerインターフェースは、

  • ProcessRequestメソッド
  • IsReusableプロパティ

を持っています。これらのメンバーを実装します。

ProcessRequestメソッドには、HTTPリクエストの応答処理を記述します。

今回のアプリケーションでは、deltパラメータをCookieから、リソースへのURLを「ImageHandler.ashx?url=[リソースへのURL]のurlパラメータから取得し、これまでと同様にAPIを利用してデータを得ます。ただし、XMLではなく画像のバイナリデータを取得することになります。

まず、リクエスト作成までのコードは次のようになります。

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に指定されたパラメータは、メソッドの引数HttpContextオブジェクトのRequestプロパティから取得が可能です。もし取得できなかった場合は、Bad Requestとして応答し処理を終了しています。リソースプロバイダーへのアクセスはXML文書取得のときに示したものと同じですね。

上記のコードに、フォトのバイナリデータを取得して、そのデータをそのままレスポンスとして返すコードを追記します。

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.Responseプロパティを使って設定します。まず、ContentTypeをリクエストにより得られたフォトバイナリデータのContentTypeをそのまま指定しています。次に、バイナリデータを読み取り、OutputStreamプロパティを使ってそのレスポンスのデータを書き込みます。

IsReusableプロパティは、このファイルへ別の要求が来た場合に、インスタンス済みのIHttpHandlerオブジェクトを使用できるかを示すために使用されます。今回は再利用できますのでTrueを返します。

Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable
    Get
        Return True
    End Get
End Property

以上で画像を返すページができました。

Default.aspx.vbファイルに戻り、フォトタイプのリストボックスの項目が選択されたときの処理を追加しましょう。

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を指定し、作成したImageHandler.ashxにアクセスするようにしています。

いかがでしたでしょうか。今回作成するWebアプリケーションは以上で完成です。実行して動作を確認してみましょう。Live IDアカウントによるサインイン後、アルバムの選択、フォトの選択、フォトのタイプの選択を行うと、その画像が表示されたでしょうか。次回もPhoto APIについて紹介する予定です。

おすすめ記事

記事・ニュース一覧