Share via


Exibir dados binários nos controles de dados da Web (C#)

por Scott Mitchell

Baixar PDF

Neste tutorial, analisamos as opções para apresentar dados binários em uma página da Web, incluindo a exibição de um arquivo de imagem e o provisionamento de um link 'Download' para um arquivo PDF.

Introdução

No tutorial anterior, exploramos as duas técnicas para associar dados binários a um modelo de dados subjacente de um aplicativo e usamos o controle FileUpload para carregar arquivos de um navegador para o sistema de arquivos do servidor Web. Ainda não vimos como associar os dados binários carregados ao modelo de dados. Ou seja, depois que um arquivo for carregado e salvo no sistema de arquivos, um caminho para o arquivo deverá ser armazenado no registro de banco de dados apropriado. Se os dados estiverem sendo armazenados diretamente no banco de dados, os dados binários carregados não precisarão ser salvos no sistema de arquivos, mas deverão ser injetados no banco de dados.

No entanto, antes de examinarmos como associar os dados ao modelo de dados, vamos primeiro examinar como fornecer os dados binários ao usuário final. Apresentar dados de texto é bastante simples, mas como os dados binários devem ser apresentados? Depende, é claro, do tipo de dados binários. Para imagens, provavelmente queremos exibir a imagem; para PDFs, documentos do Microsoft Word, arquivos ZIP e outros tipos de dados binários, fornecer um link de Download provavelmente é mais apropriado.

Neste tutorial, veremos como apresentar os dados binários junto com seus dados de texto associados usando controles da Web de dados como GridView e DetailsView. No próximo tutorial, voltaremos nossas atenções para associar um arquivo carregado ao banco de dados.

Etapa 1: fornecendoBrochurePathvalores

A Picture coluna na Categories tabela já contém dados binários para as várias imagens de categoria. Especificamente, a Picture coluna de cada registro contém o conteúdo binário de uma imagem de bitmap granular, de baixa qualidade e de 16 cores. Cada imagem de categoria tem 172 pixels de largura e 120 pixels de altura e consome aproximadamente 11 KB. Além disso, o conteúdo binário na Picture coluna inclui um cabeçalho OLE de 78 bytes que deve ser removido antes de exibir a imagem. Essas informações de cabeçalho estão presentes porque o banco de dados Northwind tem suas raízes no Microsoft Access. No Access, os dados binários são armazenados usando o tipo de dados Objeto OLE, que aborda esse cabeçalho. Por enquanto, veremos como remover os cabeçalhos dessas imagens de baixa qualidade para exibir a imagem. Em um tutorial futuro, criaremos uma interface para atualizar uma coluna de Picture categoria e substituiremos essas imagens de bitmap que usam cabeçalhos OLE por imagens JPG equivalentes sem os cabeçalhos OLE desnecessários.

No tutorial anterior, vimos como usar o controle FileUpload. Portanto, você pode ir em frente e adicionar arquivos de folheto ao sistema de arquivos do servidor Web. No entanto, isso não atualiza a BrochurePath coluna na Categories tabela. No próximo tutorial, veremos como fazer isso, mas por enquanto precisamos fornecer valores manualmente para essa coluna.

No download deste tutorial, você encontrará sete arquivos de folheto PDF na ~/Brochures pasta, um para cada uma das categorias, exceto Frutos do Mar. Omiti propositalmente a adição de um folheto de frutos do mar para ilustrar como lidar com cenários em que nem todos os registros têm dados binários associados. Para atualizar a Categories tabela com esses valores, clique com o botão direito do Categories mouse no nó do Servidor Explorer e escolha Mostrar Dados da Tabela. Em seguida, insira os caminhos virtuais para os arquivos de folheto para cada categoria que tenha um folheto, como mostra a Figura 1. Como não há nenhum folheto para a categoria Frutos do Mar, deixe o BrochurePath valor da coluna como NULL.

Inserir manualmente os valores para a coluna BrochurePath da Tabela de Categorias

Figura 1: Insira manualmente os valores da Categories coluna da BrochurePath tabela (clique para exibir a imagem em tamanho real)

Com os BrochurePath valores fornecidos para a Categories tabela, estamos prontos para criar um GridView que lista cada categoria junto com um link para baixar o folheto da categoria. Na Etapa 4, estenderemos este GridView para exibir também a imagem da categoria.

Comece arrastando um GridView da Caixa de Ferramentas para o Designer da DisplayOrDownloadData.aspx página na BinaryData pasta . Defina gridView s ID como Categories e por meio da marca inteligente GridView, escolha associá-lo a uma nova fonte de dados. Especificamente, associe-o a um ObjectDataSource chamado CategoriesDataSource que recupera dados usando o CategoriesBLL método do objeto.GetCategories()

Criar um novo objetoDataSource chamado CategoriesDataSource

Figura 2: Criar um novo objectDataSource chamado CategoriesDataSource (clique para exibir a imagem em tamanho real)

Configurar o ObjectDataSource para usar a classe CategoriesBLL

Figura 3: Configurar o ObjectDataSource para usar a CategoriesBLL classe (clique para exibir a imagem em tamanho real)

Recuperar a lista de categorias usando o método GetCategories()

Figura 4: Recuperar a lista de categorias usando o GetCategories() método (clique para exibir a imagem em tamanho real)

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio adicionará automaticamente um BoundField ao Categories GridView para , CategoryIDCategoryNameDescription, NumberOfProducts, e BrochurePathDataColumn s. Vá em frente e remova BoundField NumberOfProducts , pois a GetCategories() consulta do método não recupera essas informações. Remova também BoundField CategoryID e renomeie as CategoryName propriedades e BrochurePath BoundFields HeaderText como Category e Brochure, respectivamente. Depois de fazer essas alterações, a marcação declarativa de GridView e ObjectDataSource deve ser semelhante à seguinte:

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

Exiba esta página por meio de um navegador (consulte a Figura 5). Cada uma das oito categorias está listada. As sete categorias com BrochurePath valores têm o BrochurePath valor exibido no respectivo BoundField. Frutos do mar, que tem um NULL valor para seu BrochurePath, exibe uma célula vazia.

O nome, a descrição e o valor de BrochurePath de cada categoria são listados

Figura 5: o nome, a descrição e o BrochurePath valor de cada categoria são listados (clique para exibir a imagem em tamanho real)

Em vez de exibir o texto da BrochurePath coluna, queremos criar um link para o folheto. Para fazer isso, remova BoundField e substitua-o BrochurePath por um HyperLinkField. Defina a nova propriedade HyperLinkField como HeaderText Brochure, sua Text propriedade como View Brochure e sua DataNavigateUrlFields propriedade como BrochurePath.

Adicionar um HyperLinkField para BrochurePath

Figura 6: Adicionar um HyperLinkField para BrochurePath

Isso adicionará uma coluna de links ao GridView, como mostra a Figura 7. Clicar em um link Exibir Folheto exibirá o PDF diretamente no navegador ou solicitará que o usuário baixe o arquivo, dependendo se um leitor de PDF está instalado e as configurações do navegador.

Um folheto de categoria pode ser exibido clicando no link Exibir Folheto

Figura 7: Um folheto de categoria pode ser exibido clicando no link Exibir Folheto (clique para exibir a imagem em tamanho real)

O PDF do Folheto da Categoria é Exibido

Figura 8: O PDF do Folheto da Categoria é exibido (clique para exibir a imagem em tamanho real)

Ocultando o texto do folheto de exibição para categorias sem um folheto

Como mostra a Figura 7, o BrochurePath HyperLinkField exibe seu Text valor de propriedade ( View Brochure ) para todos os registros, independentemente de haver um valor nãoNULL para BrochurePath. Claro, se BrochurePath for NULL, o link será exibido apenas como texto, como é o caso da categoria Frutos do Mar (consulte a Figura 7). Em vez de exibir o texto Folheto de Exibição, pode ser bom ter essas categorias sem um BrochurePath valor exibindo algum texto alternativo, como Sem Folheto Disponível.

Para fornecer esse comportamento, precisamos usar um TemplateField cujo conteúdo é gerado por meio de uma chamada para um método de página que emite a saída apropriada com base no BrochurePath valor . Primeiro, exploramos essa técnica de formatação no tutorial Usando TemplateFields no Controle GridView .

Transforme o HyperLinkField em um TemplateField selecionando o BrochurePath HyperLinkField e clicando no link Converter este campo em um TemplateField na caixa de diálogo Editar Colunas.

Converter o HyperLinkField em um TemplateField

Figura 9: Converter o HyperLinkField em um TemplateField

Isso criará um TemplateField com um ItemTemplate que contém um controle Da Web HyperLink cuja NavigateUrl propriedade está associada ao BrochurePath valor . Substitua essa marcação por uma chamada para o método GenerateBrochureLink, passando o valor de BrochurePath:

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

Em seguida, crie um protected método na classe code-behind da página ASP.NET chamada GenerateBrochureLink que retorna um string e aceita um object como um parâmetro de entrada.

protected string GenerateBrochureLink(object BrochurePath)
{
    if (Convert.IsDBNull(BrochurePath))
        return "No Brochure Available";
    else
        return string.Format(@"<a href="{0}">View Brochure</a>", 
            ResolveUrl(BrochurePath.ToString()));
}

Esse método determina se o valor passado object é um banco de dados NULL e, nesse caso, retorna uma mensagem indicando que a categoria não tem um folheto. Caso contrário, se houver um BrochurePath valor, ele será exibido em um hiperlink. Observe que, se o BrochurePath valor estiver presente, ele será passado para o ResolveUrl(url) método . Esse método resolve a URL passada, substituindo o ~ caractere pelo caminho virtual apropriado. Por exemplo, se o aplicativo tiver raiz em /Tutorial55, ResolveUrl("~/Brochures/Meats.pdf") retornará /Tutorial55/Brochures/Meat.pdf.

A Figura 10 mostra a página depois que essas alterações foram aplicadas. Observe que o campo da categoria Frutos do BrochurePath Mar agora exibe o texto Sem Folheto Disponível.

O texto sem folheto disponível é exibido para essas categorias sem um folheto

Figura 10: o texto sem folheto disponível é exibido para essas categorias sem um folheto (clique para exibir a imagem em tamanho real)

Etapa 3: Adicionar uma página da Web para exibir uma imagem de categoria

Quando um usuário visita uma página ASP.NET, ele recebe o HTML da página ASP.NET. O HTML recebido é apenas texto e não contém dados binários. Quaisquer dados binários adicionais, como imagens, arquivos de som, aplicativos Macromedia Flash, vídeos Reprodutor Multimídia do Windows inseridos e assim por diante, existem como recursos separados no servidor Web. O HTML contém referências a esses arquivos, mas não inclui o conteúdo real dos arquivos.

Por exemplo, em HTML, o <img> elemento é usado para fazer referência a uma imagem, com o src atributo apontando para o arquivo de imagem da seguinte forma:

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

Quando um navegador recebe esse HTML, ele faz outra solicitação ao servidor Web para recuperar o conteúdo binário do arquivo de imagem, que ele então exibe no navegador. O mesmo conceito se aplica a qualquer dado binário. Na Etapa 2, o folheto não foi enviado para o navegador como parte da marcação HTML da página. Em vez disso, o HTML renderizado forneceu hiperlinks que, quando clicados, fizeram com que o navegador solicitasse o documento PDF diretamente.

Para exibir ou permitir que os usuários baixem dados binários que residem no banco de dados, precisamos criar uma página da Web separada que retorne os dados. Para nosso aplicativo, há apenas um campo de dados binário armazenado diretamente no banco de dados da imagem da categoria. Portanto, precisamos de uma página que, quando chamada, retorne os dados de imagem para uma categoria específica.

Adicione uma nova página ASP.NET à BinaryData pasta chamada DisplayCategoryPicture.aspx. Ao fazer isso, deixe a caixa de seleção Selecionar master página desmarcada. Esta página espera um CategoryID valor na querystring e retorna os dados binários dessa coluna de categoria.Picture Como esta página retorna dados binários e nada mais, ela não precisa de nenhuma marcação na seção HTML. Portanto, clique na guia Origem no canto inferior esquerdo e remova toda a marcação da página, exceto a <%@ Page %> diretiva . Ou seja, DisplayCategoryPicture.aspx a marcação declarativa deve consistir em uma única linha:

<%@ Page Language="C#" AutoEventWireup="true" 
    CodeFile="DisplayCategoryPicture.aspx.cs" 
    Inherits="BinaryData_DisplayCategoryPicture" %>

Se você vir o MasterPageFile atributo na diretiva , remova-o <%@ Page %> .

Na classe code-behind da página, adicione o seguinte código ao Page_Load manipulador de eventos:

protected void Page_Load(object sender, EventArgs e)
{
    int categoryID = Convert.ToInt32(Request.QueryString["CategoryID"]);
    // Get information about the specified category
    CategoriesBLL categoryAPI = new CategoriesBLL();
    Northwind.CategoriesDataTable categories = 
        categoryAPI.GetCategoryWithBinaryDataByCategoryID(categoryID);
    Northwind.CategoriesRow category = 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 int OleHeaderLength = 78;
    int strippedImageLength = category.Picture.Length - OleHeaderLength;
    byte[] strippedImageData = new byte[strippedImageLength];
    Array.Copy(category.Picture, OleHeaderLength, 
        strippedImageData, 0, strippedImageLength);
    
    Response.BinaryWrite(strippedImageData);
}

Esse código começa lendo no CategoryID valor de querystring em uma variável chamada categoryID. Em seguida, os dados da imagem são recuperados por meio de uma chamada para o CategoriesBLL método da classe.GetCategoryWithBinaryDataByCategoryID(categoryID) Esses dados são retornados ao cliente usando o Response.BinaryWrite(data) método , mas antes que isso seja chamado, o Picture cabeçalho OLE do valor da coluna deve ser removido. Isso é feito criando uma byte matriz chamada strippedImageData que conterá precisamente 78 caracteres a menos do que o que está na Picture coluna. O Array.Copy método é usado para copiar os dados da category.Picture posição 78 strippedImageDatapara .

A Response.ContentType propriedade especifica o tipo MIME do conteúdo que está sendo retornado para que o navegador saiba como renderizá-lo. Como a Categories coluna da Picture tabela é uma imagem bitmap, o tipo MIME de bitmap é usado aqui (image/bmp). Se você omitir o tipo MIME, a maioria dos navegadores ainda exibirá a imagem corretamente porque eles podem inferir o tipo com base no conteúdo dos dados binários do arquivo de imagem. No entanto, é prudente incluir o tipo MIME quando possível. Consulte o site da Autoridade de Números Atribuídos à Internet para obter uma listagem completa dos tipos de mídia MIME.

Com essa página criada, uma determinada imagem de categoria pode ser exibida visitando DisplayCategoryPicture.aspx?CategoryID=categoryID. A Figura 11 mostra a imagem da categoria Bebidas, que pode ser exibida de DisplayCategoryPicture.aspx?CategoryID=1.

A Imagem da Categoria de Bebidas é Exibida

Figura 11: a imagem da categoria bebidas é exibida (clique para exibir a imagem em tamanho real)

Se, ao visitar DisplayCategoryPicture.aspx?CategoryID=categoryID, você receber uma exceção que lê Não é possível converter o objeto do tipo 'System.DBNull' para digitar 'System.Byte[]', há duas coisas que podem estar causando isso. Primeiro, a Categories coluna da Picture tabela permite NULL valores. A DisplayCategoryPicture.aspx página, no entanto, pressupõe que há um valor nãoNULL presente. A Picture propriedade do CategoriesDataTable não poderá ser acessada diretamente se tiver um NULL valor. Se você quiser permitir NULL valores para a Picture coluna, convém incluir a seguinte condição:

if (category.IsPictureNull())
{
    // 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 ...
}

O código acima pressupõe que haja algum arquivo de imagem chamado NoPictureAvailable.gif na Images pasta que você deseja exibir para essas categorias sem uma imagem.

Essa exceção também poderá ser causada se a CategoriesTableAdapter instrução s do SELECT método s GetCategoryWithBinaryDataByCategoryID tiver revertido para a lista de colunas da consulta main, o que pode acontecer se você estiver usando instruções SQL ad hoc e executar novamente o assistente para a consulta main do TableAdapter. Verifique se a GetCategoryWithBinaryDataByCategoryID instrução do método s SELECT ainda inclui a Picture coluna .

Observação

Toda vez que o DisplayCategoryPicture.aspx é visitado, o banco de dados é acessado e os dados de imagem da categoria especificada são retornados. No entanto, se a imagem da categoria não tiver sido alterada desde que o usuário a viu pela última vez, isso será um esforço desperdiçado. Felizmente, HTTP permite GETs condicionais. Com um GET condicional, o cliente que está fazendo a solicitação HTTP envia um If-Modified-Since cabeçalho HTTP que fornece a data e hora em que o cliente recuperou esse recurso pela última vez do servidor Web. Se o conteúdo não tiver sido alterado desde essa data especificada, o servidor Web poderá responder com um código de status não modificado (304) e renunciar ao envio do conteúdo do recurso solicitado. Em suma, essa técnica aliviará o servidor Web de ter que enviar conteúdo de volta para um recurso se ele não tiver sido modificado desde a última vez que o cliente o acessou.

Para implementar esse comportamento, no entanto, exige que você adicione uma PictureLastModified coluna à Categories tabela a ser capturada quando a Picture coluna foi atualizada pela última vez, bem como o código para marcar para o If-Modified-Since cabeçalho. Para obter mais informações sobre o If-Modified-Since cabeçalho e o fluxo de trabalho GET condicional, consulte HTTP Conditional GET for RSS Hackers and A Deeper Look at Performing HTTP Requests in an ASP.NET Page.

Etapa 4: Exibindo as imagens de categoria em um GridView

Agora que temos uma página da Web para exibir uma determinada imagem de categoria, podemos exibi-la usando o controle Web image ou um elemento HTML <img> apontando para DisplayCategoryPicture.aspx?CategoryID=categoryID. Imagens cuja URL é determinada por dados de banco de dados podem ser exibidas em GridView ou DetailsView usando o ImageField. O ImageField contém DataImageUrlField propriedades e DataImageUrlFormatString que funcionam como as propriedades e DataNavigateUrlFormatString HyperLinkFieldDataNavigateUrlFields.

Vamos aumentar o Categories GridView adicionando DisplayOrDownloadData.aspx um ImageField para mostrar a imagem de cada categoria. Basta adicionar o ImageField e definir suas DataImageUrlField propriedades e DataImageUrlFormatString como CategoryID e DisplayCategoryPicture.aspx?CategoryID={0}, respectivamente. Isso criará uma coluna GridView que renderiza um <img> elemento cujo src atributo faz referência a DisplayCategoryPicture.aspx?CategoryID={0}, em que {0} é substituído pelo valor da CategoryID linha GridView.

Adicionar um ImageField ao GridView

Figura 12: Adicionar um ImageField ao GridView

Depois de adicionar o ImageField, a sintaxe declarativa do GridView deve ter a seguinte aparência:

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

Reserve um momento para exibir esta página por meio de um navegador. Observe como cada registro agora inclui uma imagem para a categoria.

A imagem da categoria é exibida para cada linha

Figura 13: a imagem da categoria é exibida para cada linha (clique para exibir a imagem em tamanho real)

Resumo

Neste tutorial, examinamos como apresentar dados binários. A forma como os dados são apresentados depende do tipo de dados. Para os arquivos de folheto PDF, oferecemos ao usuário um link Exibir Folheto que, quando clicado, levou o usuário diretamente para o arquivo PDF. Para a imagem da categoria, primeiro criamos uma página para recuperar e retornar os dados binários do banco de dados e, em seguida, usamos essa página para exibir cada imagem de categoria em um GridView.

Agora que analisamos como exibir dados binários, estamos prontos para examinar como executar inserções, atualizações e exclusões no banco de dados com os dados binários. No próximo tutorial, veremos como associar um arquivo carregado ao registro de banco de dados correspondente. No tutorial seguinte, veremos como atualizar os dados binários existentes, bem como excluir os dados binários quando seu registro associado for removido.

Programação feliz!

Sobre o autor

Scott Mitchell, autor de sete livros do ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Microsoft Web desde 1998. Scott trabalha como consultor independente, treinador e escritor. Seu último livro é Sams Teach Yourself ASP.NET 2.0 em 24 Horas. Ele pode ser contatado em mitchell@4GuysFromRolla.com. ou através de seu blog, que pode ser encontrado em http://ScottOnWriting.NET.

Agradecimentos Especiais

Esta série de tutoriais foi revisada por muitos revisores úteis. Os principais revisores deste tutorial foram Teresa Murphy e Dave Gardner. Interessado em revisar meus próximos artigos do MSDN? Nesse caso, solte-me uma linha em mitchell@4GuysFromRolla.com.