Exibir dados binários nos controles de dados da Web (C#)
por Scott Mitchell
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 é 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 precisarão ser salvos no sistema de arquivos, mas deverão ser injetados no banco de dados.
Antes de examinarmos a associação dos dados ao modelo de dados, no entanto, 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 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: FornecendoBrochurePath
valores
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 granulada e de baixa qualidade 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 OLE Object, que se aproxima desse 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 da categoria e substituiremos essas imagens de 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. 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 manualmente os valores para essa coluna.
No download deste tutorial, você encontrará sete arquivos de brochuras em 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 mouse no Categories
nó do Gerenciador de Servidores e escolha Mostrar Dados da Tabela. Em seguida, insira os caminhos virtuais para os arquivos de folhetos para cada categoria que tenha um folheto, como mostra a Figura 1. Como não há brochura para a categoria Frutos do mar, deixe o BrochurePath
valor da coluna como NULL
.
Figura 1: Inserir manualmente os valores da coluna da BrochurePath
tabela (clique para exibir a Categories
imagem em tamanho real)
Etapa 2: Fornecendo um link de download para os folhetos em um GridView
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 esse 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 o GridView s ID
para Categories
e por meio da marca inteligente do GridView, opte por 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 GetCategories()
objeto.
Figura 2: Criar um novo ObjectDataSource chamado CategoriesDataSource
(clique para exibir a imagem em tamanho real)
Figura 3: Configurar o ObjectDataSource para usar a classe (clique para exibir a CategoriesBLL
imagem em tamanho completo)
Figura 4: Recuperar a lista de categorias usando o método (clique para exibir a GetCategories()
imagem em tamanho real)
Depois de concluir o assistente Configurar Fonte de Dados, o Categories
Visual Studio adicionará automaticamente um BoundField ao GridView para o CategoryID
, CategoryName
, Description
, NumberOfProducts
e BrochurePath
DataColumn
s. 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 CategoryName
propriedades e BrochurePath
BoundFields HeaderText
para Category e Brochure, respectivamente. Depois de fazer essas alterações, a marcação declarativa do GridView e do 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>
Visualize esta página por meio de um navegador (consulte a Figura 5). Cada uma das oito categorias é 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.
Figura 5: O nome, a descrição e BrochurePath
o valor de cada categoria estão listados (clique para exibir a imagem em tamanho real)
Em vez de exibir o BrochurePath
texto da coluna, queremos criar um link para o folheto. Para fazer isso, remova o BrochurePath
BoundField e substitua-o por um HyperLinkField. Defina a nova propriedade do HeaderText
HyperLinkField como Brochura, sua Text
propriedade como Exibir Brochura e sua DataNavigateUrlFields
propriedade como 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.
Figura 7: Um folheto de categoria pode ser exibido clicando no link Exibir folheto (clique para exibir a imagem em tamanho real)
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 não-NULL
valor para BrochurePath
. Obviamente, 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 Exibir Brochura, pode ser bom fazer com que essas categorias sem um BrochurePath
valor exibam algum texto alternativo, como Nenhuma Brochura 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. Exploramos essa técnica de formatação pela primeira vez 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.
Figura 9: Converter o HyperLinkField em um TemplateField
Isso criará um TemplateField com um ItemTemplate
que contém um controle 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 é um banco de dados NULL
e, em object
caso afirmativo, 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 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 mudanças. Observe que o campo da categoria Frutos BrochurePath
do mar agora exibe o texto Nenhum folheto disponível.
Figura 10: O texto Nenhum folheto disponível é exibido para as categorias sem um folheto (clique para exibir a imagem em tamanho real)
Etapa 3: Adicionando uma página da Web para exibir uma imagem da 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 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 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 é exibido 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 nosso aplicativo, há apenas um campo de dados binário armazenado diretamente no banco de dados, a imagem da categoria. Portanto, precisamos de uma página que, quando chamada, retorne os dados da imagem para uma determinada categoria.
Adicione uma nova página 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 e retorna os dados binários dessa coluna da Picture
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 <%@ Page %>
diretiva. Ou seja, DisplayCategoryPicture.aspx
a marcação declarativa de s 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 <%@ Page %>
diretiva, remova-o.
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 o valor da CategoryID
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 GetCategoryWithBinaryDataByCategoryID(categoryID)
classe. Esses dados são retornados ao cliente usando o Response.BinaryWrite(data)
método, mas antes de ser 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 Picture
que está na coluna. O Array.Copy
método é usado para copiar os dados da category.Picture
posição 78 para strippedImageData
.
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 tabela é uma imagem de Picture
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 Internet Assigned Numbers Authority para obter uma lista completa dos tipos de mídia MIME.
Com esta página criada, a 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
.
Figura 11: A imagem da categoria de bebidas é exibida (clique para exibir a imagem em tamanho real)
Se, ao visitar DisplayCategoryPicture.aspx?CategoryID=categoryID
o , você receber uma exceção que diz Não é possível converter o objeto do tipo 'System.DBNull' para o tipo '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, assume que há um não-valorNULL
presente. A Picture
propriedade do CategoriesDataTable
não pode ser acessada diretamente se tiver um NULL
valor. Se você 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 poderá ser causada se a CategoriesTableAdapter
instrução do SELECT
método s GetCategoryWithBinaryDataByCategoryID
tiver sido revertida para a lista de colunas da consulta principal, o que pode acontecer se você estiver usando instruções SQL ad hoc e tiver executado novamente o assistente para a consulta principal do TableAdapter. Verifique se GetCategoryWithBinaryDataByCategoryID
a instrução do SELECT
método ainda inclui a Picture
coluna.
Observação
Sempre 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 a última vez que o usuário a visualizou, isso será 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 essa data especificada, o servidor Web poderá responder com um código de status Não modificado (304) e renunciar ao envio de volta do 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, é necessário adicionar 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 If-Modified-Since
cabeçalho e o fluxo de trabalho GET condicional, consulte HTTP Conditional GET for RSS Hackers e Uma análise mais profunda da execução de solicitações HTTP em uma página ASP.NET.
Etapa 4: Exibindo as imagens de categoria em um GridView
Agora que temos uma página da Web para exibir uma imagem de categoria específica, podemos exibi-la usando o controle Image Web ou um elemento HTML <img>
apontando para DisplayCategoryPicture.aspx?CategoryID=categoryID
. As imagens cuja URL é determinada pelos dados do banco de dados podem ser exibidas no GridView ou no DetailsView usando o ImageField. O ImageField contém DataImageUrlField
propriedades e DataImageUrlFormatString
que funcionam como as propriedades e DataNavigateUrlFields
DataNavigateUrlFormatString
HyperLinkField.
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 cujas src
referências DisplayCategoryPicture.aspx?CategoryID={0}
de atributo , onde {0} é substituído pelo valor da CategoryID
linha GridView.
Figura 12: Adicionar um ImageField ao GridView
Depois de adicionar o ImageField, a sintaxe declarativa do GridView deve ser semelhante a acalmar o 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: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 por meio de um navegador. Observe como cada registro agora inclui uma imagem para a categoria.
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 brochura em PDF, oferecemos ao usuário um link Exibir brochura que, quando clicado, levava o usuário diretamente ao 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 da categoria em um GridView.
Agora que vimos 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 depois disso, veremos como atualizar os dados binários existentes, bem como excluir os dados binários quando seu registro associado for removido.
Boa programação!
Sobre o autor
Scott Mitchell, autor de sete livros ASP/ASP.NET e fundador da 4GuysFromRolla.com, trabalha com tecnologias da Web da Microsoft desde 1998. Scott trabalha como consultor, instrutor e escritor independente. 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 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 revisar meus próximos artigos do MSDN? Em caso afirmativo, envie-me uma mensagem para mitchell@4GuysFromRolla.com.