Partilhar via


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

por Scott Mitchell

Descarregar PDF

Neste tutorial, examinamos 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 fornecimento de um link 'Download' para um arquivo PDF.

Introdução

No tutorial anterior, exploramos as duas técnicas para associar dados binários ao 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 deve 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 precisam ser salvos no sistema de arquivos, mas devem ser injetados no banco de dados.

Antes de analisarmos a associação dos dados ao modelo de dados, porém, vamos primeiro ver 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 Download é provavelmente mais apropriado.

Neste tutorial, veremos como apresentar os dados binários ao lado de seus dados de texto associados usando controles da Web de dados como GridView e DetailsView. No próximo tutorial, voltaremos nossa atenção 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 bitmap granulada, de baixa qualidade e 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 acrescenta 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 categoria e substituir essas imagens bitmap que usam cabeçalhos OLE por imagens JPG equivalentes sem os cabeçalhos Picture OLE desnecessários.

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

No download deste tutorial, você encontrará sete arquivos de brochura em PDF na ~/Brochures pasta, um para cada uma das categorias, exceto frutos do mar. Eu propositadamente omiti 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 no nó do Gerenciador de Servidores e escolha Mostrar Dados da Tabela. Em seguida, insira os caminhos virtuais para os arquivos de brochura para cada categoria que tenha um folheto, como mostra a Figura 1. Uma vez que não existe uma brochura para a categoria de Marisco, deixe o valor da sua BrochurePath coluna como NULL.

Insira manualmente os valores para a coluna BrochurePath da tabela de categorias

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

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

Comece por arrastar um GridView da caixa de ferramentas para o designer da DisplayOrDownloadData.aspx página na BinaryData pasta. Defina o GridView s ID como Categories e através da marca inteligente do GridView, opte por vinculá-lo a uma nova fonte de dados. Especificamente, associe-o a um ObjectDataSource chamado CategoriesDataSource que recupera dados usando o método CategoriesBLL do objeto GetCategories().

Criar um novo ObjectDataSource chamado CategoriesDataSource

Figura 2: Criar um novo ObjectDataSource nomeado CategoriesDataSource (Clique para visualizar a imagem em tamanho real)

Configurar o ObjectDataSource para usar a classe CategoriesBLL

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

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

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

Depois de concluir o assistente Configurar Fonte de Dados, o Visual Studio adicionará automaticamente um BoundField ao Categories GridView para CategoryID, CategoryName, Description, NumberOfProducts e BrochurePathDataColumns. Vá em frente e remova o NumberOfProducts BoundField, pois a GetCategories() consulta do método não recupera essas informações. Remova também o CategoryID BoundField e renomeie as propriedades dos CategoryName e BrochurePath BoundFields para Category e Brochure, respectivamente. Depois de fazer essas alterações, a marcação declarativa de GridView e ObjectDataSource 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: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>

Visualize esta página através de um navegador (consulte a Figura 5). Cada uma das oito categorias é listada. As sete categorias com valores BrochurePath têm o valor BrochurePath exibido no respetivo BoundField. Os frutos do mar, que têm um NULL valor para o seu BrochurePath, exibem uma célula vazia.

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

Figura 5: Nome, descrição e BrochurePath valor de cada categoria estão listados (Clique para visualizar a imagem em tamanho real)

Em vez de exibir o BrochurePath texto da coluna, queremos criar um link para a brochura. Para fazer isso, remova o BoundField e substitua-o BrochurePath por um HyperLinkField. Defina a nova propriedade HyperLinkField s HeaderText como 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 brochura 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.

A brochura de uma categoria pode ser visualizada clicando no link Ver brochura

Figura 7: Uma brochura de categoria pode ser visualizada clicando no link Exibir brochura (Clique para visualizar a imagem em tamanho real)

O PDF da brochura da categoria é apresentado

Figura 8: O PDF da brochura da categoria é exibido (Clique para visualizar a imagem em tamanho real)

Ocultar o texto "Ver Brochura" para categorias sem uma brochura

Como mostra a Figura 7, o BrochurePath HyperLinkField exibe seu Text valor de propriedade ( View Brochure ) para todos os registros, independentemente de haver um não-valorNULL para BrochurePath. Claro, se BrochurePath for NULL, então o link é exibido apenas como texto, como é o caso da categoria de frutos do mar (consulte a Figura 7). Em vez de exibir o texto View Brochure, pode ser bom que essas categorias sem um BrochurePath valor exibam algum texto alternativo, como No Brochure Available.

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 controlo Web HyperLink cuja propriedade NavigateUrl está ligada ao valor BrochurePath. 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 de 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()));
}

Este método determina se o object valor passado representa um banco de dados NULL e, em caso afirmativo, retorna uma mensagem indicando que a categoria não possui 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 é 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 estiver enraizado em /Tutorial55, ResolveUrl("~/Brochures/Meats.pdf") retornará /Tutorial55/Brochures/Meat.pdf.

A Figura 10 mostra a página após a aplicação dessas alterações. Observe que o campo s da categoria de BrochurePath frutos do mar agora exibe o texto Sem Brochura Disponível .

O texto Nenhuma brochura disponível é mostrado para as categorias que não têm uma brochura

Figura 10: O texto Nenhum folheto disponível é exibido para as categorias sem um folheto (Clique para visualizar a imagem em tamanho real)

Etapa 3: Adicionando 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, ficheiros de som, aplicações Macromedia Flash, vídeos incorporados do Windows Media Player 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 assim:

<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 exibe no navegador. O mesmo conceito se aplica a quaisquer dados binários. 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 fornecia hiperlinks que, quando clicados, faziam 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 a nossa aplicação, há apenas um campo de dados binários armazenado diretamente na base de dados: a imagem da categoria. Portanto, precisamos de uma página que, quando chamada, retorna os dados da imagem para uma determinada categoria.

Adicione uma nova página de ASP.NET à BinaryData pasta chamada DisplayCategoryPicture.aspx. Ao fazer isso, deixe a caixa de seleção Selecionar página mestra desmarcada. Esta página espera um CategoryID valor na querystring para retornar os dados binários da coluna Picture dessa categoria. 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 Fonte no canto inferior esquerdo e remova toda a marcação da página, exceto a diretiva <%@ Page %>. Ou seja, a marcação declarativa de DisplayCategoryPicture.aspx deve consistir numa única linha.

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

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

Na classe code-behind da página, adicione o seguinte código ao manipulador de Page_Load 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 o CategoryID valor 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 s da GetCategoryWithBinaryDataByCategoryID(categoryID) classe. Esses dados são retornados ao cliente usando o método Response.BinaryWrite(data), mas antes que seja utilizado, o cabeçalho OLE do valor da coluna Picture deve ser removido. Isso é feito criando uma byte matriz nomeada strippedImageData que conterá precisamente 78 caracteres a menos do que o Picture que está na coluna. O Array.Copy método é usado para copiar os dados começando na posição 78 a partir de category.Picture para strippedImageData.

A Response.ContentType propriedade especifica o tipo MIME do conteúdo que está sendo retornado para que o navegador saiba como processá-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, pois 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 sempre que possível. Consulte o site da Internet Assigned Numbers Authority para obter uma lista completa dos tipos de mídia MIME.

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

A imagem da categoria de bebidas é exibida

Figura 11: A imagem da categoria de bebidas é exibida (Clique para visualizar a imagem em tamanho real)

Se, ao visitar DisplayCategoryPicture.aspx?CategoryID=categoryID, você obtiver uma exceção que diz Não é possível converter objeto do tipo 'System.DBNull' para o tipo 'System.Byte[]', há duas coisas que podem estar causando isso. Primeiro, a coluna Categories da tabela Picture efetivamente permite NULL valores. A DisplayCategoryPicture.aspx página, no entanto, assume que há um valor não-NULL presente. A propriedade Picture de CategoriesDataTable não pode ser acedida diretamente se tiver um valor de NULL. Se quiser permitir NULL valores para a Picture coluna, inclua 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 há algum arquivo de imagem nomeado NoPictureAvailable.gif na Images pasta que você deseja exibir para essas categorias sem uma imagem.

Essa exceção também pode ser causada se a instrução CategoriesTableAdapter do GetCategoryWithBinaryDataByCategoryID método SELECT tiver sido revertida para a lista de colunas da consulta principal, o que pode acontecer se estiver a usar instruções SQL ad-hoc e tiver executado o assistente novamente para a consulta principal do TableAdapter. Verifique se a instrução do método GetCategoryWithBinaryDataByCategoryID ainda inclui a coluna SELECT.

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 mudado desde a última vez que o usuário a visualizou, isso é um esforço desperdiçado. Felizmente, o HTTP permite GETs condicionais. Com um GET condicional, o cliente que faz a solicitação HTTP envia um If-Modified-Since cabeçalho HTTP que fornece a data e a hora em que o cliente recuperou esse recurso pela última vez do servidor Web. Se o conteúdo não tiver sido alterado desde esta data especificada, o servidor Web pode responder com um código de estado Não modificado (304) e renunciar a enviar de volta o conteúdo do recurso solicitado. Em resumo, essa técnica alivia o servidor web de ter que enviar de volta o conteúdo de um recurso se ele não tiver sido modificado desde a última vez que o cliente o acessou.

Para implementar esse comportamento, no entanto, requer que você adicione uma PictureLastModified coluna à Categories tabela para capturar quando a Picture coluna foi atualizada pela última vez, bem como código para verificar o If-Modified-Since cabeçalho. Para obter mais informações sobre o cabeçalho If-Modified-Since e o fluxo de trabalho GET condicional, consulte "HTTP Conditional GET for RSS Hackers" e "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 a imagem de uma determinada categoria, podemos exibi-la usando o controle Image Web ou um elemento HTML <img> apontando para DisplayCategoryPicture.aspx?CategoryID=categoryID. As imagens cujo URL é determinado pelos dados do banco de dados podem ser exibidas no GridView ou no DetailsView usando o ImageField. O ImageField contém DataImageUrlField e DataImageUrlFormatString propriedades que funcionam como o HyperLinkField s DataNavigateUrlFields e DataNavigateUrlFormatString propriedades.

Vamos aumentar o Categories GridView em DisplayOrDownloadData.aspx ao adicionar 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 elemento <img> cujo atributo src faz referência a DisplayCategoryPicture.aspx?CategoryID={0}, onde {0} é substituído pelo valor de CategoryID da 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 visualizar esta página através 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 visualizar a imagem em tamanho normal)

Resumo

Neste tutorial, examinamos como apresentar dados binários. A forma como os dados são apresentados depende do tipo de dados. Para os ficheiros da brochura em PDF, oferecemos ao utilizador um link Ver Brochura que, quando clicado, levava o utilizador diretamente para o ficheiro 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 a imagem de cada categoria em um GridView.

Agora que analisamos como exibir dados binários, estamos prontos para examinar como executar inserção, 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 depois disso, veremos como atualizar os dados binários existentes, bem como excluir os dados binários quando seu registro associado for removido.

Feliz Programação!

Sobre o Autor

Scott Mitchell, autor de sete livros sobre ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias Web da Microsoft desde 1998. Scott trabalha como consultor, formador e escritor independente. Seu último livro é Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Ele pode ser contatado em mitchell@4GuysFromRolla.com.

Um agradecimento especial a

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