Share via


データ Web コントロールにバイナリ データを表示する (VB)

作成者: Scott Mitchell

PDF のダウンロード

このチュートリアルでは、画像ファイルの表示や PDF ファイルの [ダウンロード] リンクのプロビジョニングなど、バイナリ データを Web ページに表示するオプションについて説明します。

はじめに

前のチュートリアルでは、バイナリ データをアプリケーションの基になるデータ モデルに関連付ける 2 つの手法を確認し、FileUpload コントロールを使用してブラウザーから Web サーバーのファイル システムにファイルをアップロードしました。 アップロードされたバイナリ データをデータ モデルに関連付ける方法については、まだ確認していません。 すなわち、ファイルをアップロードしてファイル システムに保存した後、ファイルへのパスを適切なデータベース レコードに格納する必要があります。 データがデータベースに直接保存されている場合は、アップロードされたバイナリ データをファイル システムに保存する必要はありませんが、データベースに挿入する必要があります。

ただし、データをデータ モデルに関連付けることに目を向ける前に、まずバイナリ データをエンド ユーザーに提供する方法を確認しましょう。 テキスト データを表示することは簡単ですが、バイナリ データはどのように表示すべきでしょうか? もちろん、バイナリ データの種類によって異なります。 画像の場合はその画像を表示する必要がありますが、PDF、Microsoft Word ドキュメント、ZIP ファイル、その他の種類のバイナリ データの場合は、おそらくダウンロード リンクを提供するほうが適しているでしょう。

このチュートリアルでは、GridView や DetailsView などのデータ Web コントロールを使用して、バイナリ データを関連するテキスト データと共に表示する方法について説明します。 次のチュートリアルでは、アップロードしたファイルをデータベースに関連付けることに目を向けます。

手順 1: BrochurePath の値を指定する

Categories テーブル内の Picture 列には、さまざまなカテゴリの画像のバイナリ データが既に入っています。 具体的には、各レコードの Picture 列には、不鮮明で画質の低い 16 色のビットマップ画像のバイナリ コンテンツが保持されます。 各カテゴリの画像は幅 172 ピクセル、高さが 120 ピクセルで、約 11 KB 消費します。 さらに、Picture 列のバイナリ コンテンツには、画像を表示する前に削除する必要がある 78 バイトの OLE ヘッダーが含まれています。 このヘッダー情報が存在するのは、Northwind データベースのルーツが Microsoft Access にあるためです。 Access では、バイナリ データが OLE Object データ型を使用して保存され、そこにこのヘッダーが付加されます。 ここでは、画像を表示するために、これらの低画質の画像からヘッダーを削除する方法について説明します。 後のチュートリアルでは、カテゴリの Picture 列を更新するためのインターフェイスを構築し、OLE ヘッダーを使用するこれらのビットマップ画像を、不要な OLE ヘッダーのない同等の JPG 画像に置き換えます。

前のチュートリアルでは、FileUpload コントロールの使用方法について確認しました。 そのため、Web サーバーのファイル システムにパンフレット ファイルを追加できます。 ただし、そのようにしても、Categories テーブル内の BrochurePath 列は更新されません。 次のチュートリアルではこれを行う方法について確認しますが、ここではこの列の値を手動で指定する必要があります。

このチュートリアルのダウンロードでは、Seafood を除くカテゴリごとに 1 つ、合計 7 つの PDF パンフレット ファイルが ~/Brochures フォルダーにあります。 すべてのレコードにバイナリ データが関連付けられていないシナリオに対応する方法を示すために、Seafood のパンフレットを追加することを意図的に省きました。 これらの値で Categories テーブルを更新するには、サーバー エクスプローラーで Categories ノードを右クリックし、[テーブル データの表示] を選択します。 次に、図 1 に示すように、1 つのパンフレットが格納された各カテゴリのパンフレット ファイルへの仮想パスを入力します。 Seafood カテゴリのパンフレットはないため、BrochurePath 列の値は NULL のままにします。

Manually Enter the Values for the Categories Table s BrochurePath Column

図 1: Categories テーブルの BrochurePath 列の値を手動で入力します (クリックするとフルサイズの画像が表示されます)

Categories テーブルに BrochurePath 値が指定されたので、各カテゴリを一覧表示する GridView と、カテゴリのパンフレットをダウンロードするためのリンクを作成する準備ができました。 手順 4 では、この GridView を拡張して、カテゴリの画像も表示します。

まず、ツールボックスから BinaryData フォルダー内の DisplayOrDownloadData.aspx ページのデザイナーに GridView をドラッグします。 GridView の IDCategories に設定し、GridView のスマート タグを使用して、それを新しいデータ ソースにバインドすることを選択します。 具体的には、CategoriesBLL オブジェクトの GetCategories() メソッドを使用してデータを取得する、CategoriesDataSource という名前の ObjectDataSource にバインドします。

Create a New ObjectDataSource Named CategoriesDataSource

図 2: CategoriesDataSource という名前の 新しい ObjectDataSource を作成します (クリックするとフルサイズの画像が表示されます)

Configure the ObjectDataSource to Use the CategoriesBLL Class

図 3: CategoriesBLL クラスを使用するように ObjectDataSource を構成します (クリックするとフルサイズの画像が表示されます)

Retrieve the List of Categories Using the GetCategories() Method

図 4: GetCategories() メソッドを使用してカテゴリの一覧を取得します (クリックするとフルサイズの画像が表示されます)

データ ソースの構成ウィザードが完了すると、Visual Studio によって CategoryIDCategoryNameDescriptionNumberOfProductsBrochurePathDataColumnCategories GridView に BoundField が自動的に追加されます。 GetCategories() メソッドのクエリではこの情報が取得されないため、NumberOfProducts BoundField を削除してください。 また、CategoryID BoundField を削除し、BoundField の HeaderText プロパティ CategoryNameBrochurePath の名前をそれぞれ Category と Brochure に変更します。 これらの変更を行った後、GridView と ObjectDataSource の宣言型マークアップは次のようになります。

<asp:GridView ID="Categories" runat="server" 
    AutoGenerateColumns="False" DataKeyNames="CategoryID"
    DataSourceID="CategoriesDataSource" EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:BoundField DataField="BrochurePath" HeaderText="Brochure" 
            SortExpression="BrochurePath" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="CategoriesDataSource" runat="server" 
    OldValuesParameterFormatString="original_{0}"
    SelectMethod="GetCategories" TypeName="CategoriesBLL">
</asp:ObjectDataSource>

ブラウザーを使用してこのページを表示します (図 5 を参照)。 8 つの各カテゴリが一覧表示されます。 BrochurePath 値を持つ 7 つのカテゴリには、それぞれの BoundField に BrochurePath 値が表示されます。 BrochurePath の値が NULL の Seafood には空のセルが表示されます。

Each Category s Name, Description, and BrochurePath Value is Listed

図 5: 各カテゴリの名前、説明、および BrochurePath 値が一覧表示されます (クリックするとフルサイズの画像が表示されます)

BrochurePath 列のテキストを表示するのではなく、パンフレットへのリンクを作成します。 これを行うには、BrochurePath BoundField を削除し、HyperLinkField に置き換えます。 新しい HyperLinkField の HeaderText プロパティを Brochure に、その Text プロパティを View Brochure に、その DataNavigateUrlFields プロパティを BrochurePath に設定します。

Add a HyperLinkField for BrochurePath

図 6: BrochurePath の HyperLinkField を追加します

これにより、図 7 に示すように、GridView へのリンクの列が追加されます。 [View Brochure] リンクをクリックすると、PDF リーダーがインストールされているかどうかとブラウザーの設定に応じて、PDF がブラウザーに直接表示されるか、ユーザーにファイルのダウンロードが求められます。

A Category s Brochure Can Be Viewed by Clicking the View Brochure Link

図 7: [View Brochure] リンクをクリックすると、カテゴリのパンフレットを表示できます (クリックするとフルサイズの画像が表示されます)

The Category s Brochure PDF is Displayed

図 8: そのカテゴリのパンフレット PDF が表示されます (クリックするとフルサイズの画像が表示されます)

カタログのないカテゴリの「View Brochure」のテキストを非表示にする

図 7 に示すように、BrochurePath HyperLinkField によって、BrochurePathNULL 以外の値が指定されているかどうかに関係なく、すべてのレコードに Text プロパティ値 (View Brochure) が表示されます。 もちろん、BrochurePathNULL である場合、Seafood カテゴリの場合と同様に、リンクはテキストとしてのみ表示されます (図 7 を参照)。 BrochurePath 値のないカテゴリには、「View Brochure」というテキストではなく、「No Brochure Available」などの代替テキストを表示するほうがよいかもしれません。

この動作にするには、BrochurePath 値に基づいて適切な出力を発するページ メソッドへの呼び出しを介してコンテンツが生成される、TemplateField を使用する必要があります。 このフォーマット手法については、前の「GridView コントロールでの TemplateField の使用」のチュートリアルで初めて確認しました。

BrochurePath HyperLinkField を選択し、[列の編集] ダイアログ ボックスで [このフィールドを TemplateField に変換します] リンクをクリックして、HyperLinkField を TemplateField に変換します。

Convert the HyperLinkField into a TemplateField

図 9: HyperLinkField を TemplateField に変換します

これにより、NavigateUrl プロパティが BrochurePath 値にバインドされている HyperLink Web コントロールを含む ItemTemplate とともに、TemplateField が作成されます。 このマークアップをメソッド GenerateBrochureLink への呼び出しに置き換え、BrochurePath の値を渡します。

<asp:TemplateField HeaderText="Brochure">
    <ItemTemplate>
        <%# GenerateBrochureLink(Eval("BrochurePath")) %>
    </ItemTemplate>
</asp:TemplateField>

次に、String を返し、入力パラメーターとして Object を受け入れる GenerateBrochureLink という名前の ASP.NET ページの分離コード クラスに、Protected メソッドを作成します。

Protected Function GenerateBrochureLink(BrochurePath As Object) As String
    If Convert.IsDBNull(BrochurePath) Then
        Return "No Brochure Available"
    Else
        Return String.Format("<a href="{0}">View Brochure</a>", _
            ResolveUrl(BrochurePath.ToString()))
    End If
End Function

このメソッドは、渡された Object 値がデータベース NULL であるかどうかを判定し、そうである場合は、カテゴリにパンフレットがないことを示すメッセージを返します。 そうではなく、BrochurePath 値がある場合は、それがハイパーリンクで表示されます。 BrochurePath 値が存在する場合は、それが ResolveUrl(url) メソッドに渡されることに注意してください。 このメソッドにより、渡された URL が解決され、~ の文字が適切な仮想パスに置き換えられます。 たとえば、アプリケーションが /Tutorial55 でルート化されている場合、ResolveUrl("~/Brochures/Meats.pdf") によって /Tutorial55/Brochures/Meat.pdf が返されます。

図 10 は、これらの変更が適用された後のページを示しています。 Seafood カテゴリの BrochurePath フィールドに「No Brochure Available」というテキストが表示されるようになったことに注目してください。

The Text No Brochure Available is Displayed for Those Categories Without a Brochure

図 10: パンフレットのないカテゴリに「No Brochure Available」というテキストが表示されます (クリックするとフルサイズの画像が表示されます)

手順 3: カテゴリの画像を表示する Web ページを追加する

ユーザーが ASP.NET ページにアクセスすると、ASP.NET ページの HTML を受け取ります。 受け取った HTML はテキストに過ぎず、バイナリ データは含まれません。 画像、サウンド ファイル、Macromedia Flash アプリケーション、埋め込みの Windows メディア プレーヤーのビデオなどの追加のバイナリ データは、Web サーバー上に別個のリソースとして存在します。 HTML にはこれらのファイルへの参照は含まれていますが、ファイルの実際の内容は含まれません。

たとえば、HTML では、画像を参照するのに <img> 要素を使用し、src 属性が次のように画像ファイルをポイントします。

<img src="MyPicture.jpg" ... />

ブラウザーがこの HTML を受け取ると、Web サーバーに対して画像ファイルのバイナリ コンテンツを取得するように別の要求を行うことで、それがブラウザーに表示されます。 バイナリ データにも同じ概念が適用されます。 手順 2 では、パンフレットがページの HTML マークアップの一部としてブラウザーに送信されませんでした。 代わりに、クリックによってブラウザーが PDF ドキュメントを直接要求するハイパーリンクが、レンダリングされた HTML から提供されていました。

ユーザーがデータベース内に存在するバイナリ データを表示またはダウンロードできるようにするには、そのデータを返す別の Web ページを作成する必要があります。 このアプリケーションに関しては、データベースに直接保存されるバイナリ データ フィールドは、そのカテゴリの画像 1 つのみです。 したがって、呼び出されるとその特定のカテゴリの画像データを返すページが必要です。

DisplayCategoryPicture.aspx という名前の BinaryData フォルダーに新しい ASP.NET ページを追加します。 その際、[マスター ページを選択する] チェックボックスはオフのままにしておきます。 このページでは、querystring に CategoryID 値を要求し、そのカテゴリの Picture 列のバイナリ データを返します。 このページはバイナリ データを返し、それ以外は何も返さないため、HTML セクションにマークアップは必要ありません。 そのため、左下隅にある [ソース] タブをクリックし、<%@ Page %> ディレクティブを除くページのマークアップをすべて削除します。 つまり、DisplayCategoryPicture.aspx の宣言型マークアップは、次のように 1 行で構成する必要があります。

<%@ Page Language="VB" AutoEventWireup="true" 
    CodeFile="DisplayCategoryPicture.aspx.vb" 
    Inherits="BinaryData_DisplayCategoryPicture" %>

<%@ Page %> ディレクティブに MasterPageFile 属性がある場合は、それを削除します。

ページの分離コード クラスで、次のコードを Page_Load イベント ハンドラーに追加します。

Protected Sub Page_Load(sender As Object, e As EventArgs) Handles Me.Load
    Dim categoryID As Integer = _
        Convert.ToInt32(Request.QueryString("CategoryID"))
    ' Get information about the specified category
    Dim categoryAPI As New CategoriesBLL()
    Dim categories As Northwind.CategoriesDataTable = _
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID)
    Dim category As Northwind.CategoriesRow = categories(0)
    ' Output HTTP headers providing information about the binary data
    Response.ContentType = "image/bmp"
    ' Output the binary data
    ' But first we need to strip out the OLE header
    Const OleHeaderLength As Integer = 78
    Dim strippedImageLength As Integer = _
        category.Picture.Length - OleHeaderLength
    Dim strippedImageData(strippedImageLength) As Byte
    Array.Copy(category.Picture, OleHeaderLength, _
        strippedImageData, 0, strippedImageLength)
    Response.BinaryWrite(strippedImageData)
End Sub

このコードは、最初に CategoryID querystring 値を categoryID という名前の変数に読み込みます。 次に、画像データは CategoriesBLL クラスの GetCategoryWithBinaryDataByCategoryID(categoryID) メソッドへの呼び出しを介して取得されます。 このデータは Response.BinaryWrite(data) メソッドを使用してクライアントに返されますが、このメソッドが呼び出される前に、Picture 列値の OLE ヘッダーを削除する必要があります。 これは、Picture 列にある文字数よりもちょうど 78 文字少ない文字数を保持する、strippedImageData という名前の Byte 配列を作成することで達成されます。 Array.Copy メソッドは、category.Picture の 78 の位置から strippedImageData にデータをコピーするのに使用されます。

Response.ContentType プロパティは、コンテンツのレンダリング方法をブラウザーで認識できるように、返されるコンテンツの MIME の種類を指定しします。 Categories テーブルの Picture 列はビットマップ画像であるため、ここで使用される MIME の種類はビットマップ (image/bmp) になります。 MIME の種類を省略しても、ほとんどのブラウザーでは、画像ファイルのバイナリ データの内容に基づいて種類を推測できるため、画像を正しく表示できます。 ただし、可能であれば MIME の種類を指定するのが賢明です。 MIME メディアの種類の全一覧については、Internet Assigned Numbers Authority の Web サイトを参照してください。

このページを作成して DisplayCategoryPicture.aspx?CategoryID=categoryID にアクセスすると、特定のカテゴリの画像を表示できます。 図 11 は、Beverages カテゴリの画像を示しています。DisplayCategoryPicture.aspx?CategoryID=1 から表示できます。

The Beverages Category s Picture is Displayed

図 11: Beverages カテゴリの画像が表示されます (クリックするとフルサイズの画像が表示されます)

DisplayCategoryPicture.aspx?CategoryID=categoryID にアクセスすると「'System.DBNull' 型のオブジェクトを 'System.Byte[]' 型にキャストすることはできません」という例外が発生する場合、その原因としては 2 つのこと考えられます。 第一に、Categories テーブルの Picture 列では NULL 値が許可されます。 ただし、DisplayCategoryPicture.aspx ページでは NULL 値以外の値が存在すると想定しています。 NULL 値がある場合、CategoriesDataTablePicture プロパティに直接アクセスすることはできません。 Picture 列に NULL 値を許可する必要がある場合は、次の条件を含めます。

If category.IsPictureNull() Then
    ' Display some "No Image Available" picture
    Response.Redirect("~/Images/NoPictureAvailable.gif")
Else
    ' Send back the binary contents of the Picture column
    ' ... Set ContentType property and write out ...
    ' ... data via Response.BinaryWrite ...
End If

上記のコードでは、画像のないカテゴリに対して表示する、NoPictureAvailable.gif という名前の画像ファイルが Images フォルダーにあることを前提としています。

この例外は、CategoriesTableAdapterGetCategoryWithBinaryDataByCategoryID メソッドの SELECT ステートメントがメイン クエリの列リストに戻った場合にも発生する可能性があります。これは、アドホック SQL ステートメントを使用しており、TableAdapter のメイン クエリに対してそのウィザードを再実行したことがある場合に発生することがあります。 GetCategoryWithBinaryDataByCategoryID メソッドの SELECT ステートメントに Picture 列がまだ含まれていることを確認します。

Note

DisplayCategoryPicture.aspx にアクセスするたびに、データベースへのアクセスが発生し、指定されたカテゴリの画像データが返されます。 ただし、ユーザーが最後に表示してからカテゴリの画像が変更されていない場合、これは無駄な処理になります。 幸いなことに、HTTP では "条件付き GET" が許可されます。 条件付き GET を使用すると、HTTP 要求を行うクライアントは、クライアントが Web サーバーからこのリソースを最後に取得した日時を指定する If-Modified-Since HTTP ヘッダーに沿って送信するようになります。 この指定された日付以降にコンテンツが変更されていない場合、Web サーバーは状態コード Not Modified (304) で応答し、要求されたリソースのコンテンツの送り返すのを見送ることがあります。 つまり、この手法により、クライアントが最後にアクセスしてからリソースが変更されていない場合は、Web サーバーがリソースのコンテンツを送り返さなくて済むようになります。

ただし、この動作を実装するには、Picture 列が最後に更新されたタイミングをキャプチャする PictureLastModified 列を Categories テーブルに追加するほか、If-Modified-Since ヘッダーをチェックするコードが必要です。 If-Modified-Since ヘッダーと条件付き GET のワークフローの詳細については、「RSS ハッカー向けの HTTP 条件付き GET」と、ASP.NET ページでの HTTP 要求の実行に関する詳細を参照してください。

手順 4: GridView にカテゴリの画像を表示する

これで特定のカテゴリの画像を表示する Web ページが作成されたので、Image Web コントロールまたは DisplayCategoryPicture.aspx?CategoryID=categoryID を指している HTML <img> 要素を使用してそれを表示できます。 データベースのデータによって URL が決まる画像は、ImageField を使用して GridView または DetailsView に表示できます。 ImageField には、HyperLinkField の DataNavigateUrlFields プロパティや DataNavigateUrlFormatString プロパティと同じように機能する DataImageUrlField プロパティや DataImageUrlFormatString プロパティがあります。

各カテゴリの画像を表示するために、ImageField を追加して DisplayOrDownloadData.aspxCategories GridView を拡張してみましょう。 ImageField を追加し、その DataImageUrlField プロパティと DataImageUrlFormatString プロパティをそれぞれ CategoryIDDisplayCategoryPicture.aspx?CategoryID={0} に設定するだけです。 これにより、src 属性が DisplayCategoryPicture.aspx?CategoryID={0} を参照する <img> 要素をレンダリングする GridView 列が作成されます。ここで、{0} は GridView 行の CategoryID 値に置き換えられます。

Add an ImageField to the GridView

図 12: GridView に ImageField を追加します

ImageField を追加した後の GridView の宣言型構文は次のようになります。

<asp:GridView ID="Categories" runat="server" AutoGenerateColumns="False" 
    DataKeyNames="CategoryID" DataSourceID="CategoriesDataSource" 
    EnableViewState="False">
    <Columns>
        <asp:BoundField DataField="CategoryName" HeaderText="Category" 
            SortExpression="CategoryName" />
        <asp:BoundField DataField="Description" HeaderText="Description" 
            SortExpression="Description" />
        <asp:TemplateField HeaderText="Brochure">
            <ItemTemplate>
                <%# GenerateBrochureLink(Eval("BrochurePath")) %>
            </ItemTemplate>
        </asp:TemplateField>
        <asp:ImageField DataImageUrlField="CategoryID" 
            DataImageUrlFormatString="DisplayCategoryPicture.aspx?CategoryID={0}">
        </asp:ImageField>
    </Columns>
</asp:GridView>

少し時間を取り、ブラウザーでこのページを表示してみてください。 各レコードにカテゴリの画像が含まれるようになったことに注目してください。

The Category s Picture is Displayed for Each Row

図 13: 各行にカテゴリの画像が表示されます (クリックするとフルサイズの画像が表示されます)

まとめ

このチュートリアルでは、バイナリ データを表示する方法を確認しました。 データの表示方法は、データの種類によって異なります。 パンフレットの PDF ファイルについては、クリックするとユーザーを PDF ファイルに直接移動する [View Brochure] リンクを設定しました。 カテゴリの画像については、まずデータベースからバイナリ データを取得して返すページを作成し、そのページを使用して各カテゴリの画像を GridView に表示しました。

ここまででバイナリ データを表示する方法を確認したので、バイナリ データの入ったデータベースに対して挿入、更新、削除を実行する方法を確認する準備ができました。 次のチュートリアルでは、アップロードされたファイルを対応するデータベース レコードに関連付ける方法について説明します。 その後のチュートリアルでは、既存のバイナリ データを更新する方法と、関連するレコードが削除されたときにそのバイナリ データを削除する方法について説明します。

プログラミングに満足!

著者について

7 冊の ASP/ASP.NET 書籍の著者であり、4GuysFromRolla.com の創設者である Scott Mitchell は、1998 年から Microsoft Web テクノロジを扱っています。 Scott は、独立したコンサルタント、トレーナー、ライターとして働いています。 彼の最新の本は サムズは24時間で2.0 ASP.NET 自分自身を教えています。 にアクセスするか、ブログを使用して にアクセスmitchell@4GuysFromRolla.comできます。これは でhttp://ScottOnWriting.NET見つけることができます。

特別な感謝

このチュートリアル シリーズは、多くの役に立つ校閲者によってレビューされました。 このチュートリアルのリード レビュー担当者は、Teresa Murphy と Dave Gardner でした。 今後の MSDN の記事を確認することに関心がありますか? その場合は、 にmitchell@4GuysFromRolla.com行をドロップしてください。