Webcam

Olá meu(minha) querido(a).

Vocês gostaram do artigo que trata de capturar uma imagem através da WebCam em uma página ASP.NET, não é? E se eu me lembro bem, fiquei “devendo” um artigo que ensina como gravar esta imagem em uma base de dados, não é? Pois bem, aí vamos nós!!!

Mas antes, caso você ainda não teve a oportunidade de ver o artigo anterior, sugiro que o leia antes de adentrar à fundo neste: Capturando a imagem de uma WebCam em páginas ASP.NET.

No artigo anterior, ao clicarmos no botão que faz a captura da imagem, o applet Flash que interage com a WebCam “posta” um stream com a foto em uma página chamada “Upload.aspx”, onde processamos este stream e gravamos em um arquivo físico no servidor. O que iremos fazer agora é pegar esta mesma InputStream da página Upload.aspx e gravá-la em um campo do tipo Image (ou outro que armazene dados binários) no Banco de Dados.

Faremos uma pequena modificação na página Upload.aspx à saber:

protected void Page_Load(object sender, EventArgs e)
{
	if (!IsPostBack)
	{
		try
		{
			//Não usaremos mais a saída em uma URL, e sim guardaremos o stream de entrada em variável de sessão
			byte[] buffer = new byte[Request.InputStream.Length];
			Request.InputStream.Read(buffer, 0, (int)Request.InputStream.Length);
			SessionFacade.FotoArmazenada = buffer;
		}
		catch
		{
			Response.Clear();
			Response.Write("ERROR: Erro ao salvar imagemn");
		}
		Response.End();
	}
}

Em vez de gravar em um arquivo físico no servidor Web, pegaremos a InputStream e salvaremos em uma variável de sessão. Esta variável está encapsulada na propriedade SessionFacade.FotoArmazenada. Abaixo temos o código da classe SessionFacade (um Facade que encapsula variáveis de sessão) e da propriedade FotoArmazenada, do tipo byte[].

public class SessionFacade
{
	public static byte[] FotoArmazenada
	{
		get
		{
			if (HttpContext.Current.Session["img"] == null)
			{
				return null;
			}
			else
			{
				return (byte[])HttpContext.Current.Session["img"];
			}
		}
		set
		{
			if (HttpContext.Current.Session["img"] == null)
			{
				HttpContext.Current.Session.Add("img", value);
			}
			else
			{
				HttpContext.Current.Session["img"] = value;
			}
		}
	}
}

Vamos agora às alterações em Default.aspx:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CapturaWebcamASPNET.Default" %>

<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Exemplo de captura de imagem da WebCam usando ASP.NET e gravando em BD</title>
    <script type="text/javascript" src="camutils/webcam.js"></script>
</head>
<body>
    <form id="form1" runat="server">
	    <asp:ScriptManager runat="server" ID="sc1"></asp:ScriptManager>

        <!-- Configura algumas opções da webcam -->
	    <script type="text/javascript" language="JavaScript">
	        webcam.set_api_url('Upload.aspx');//Página de destino do arquivo capturado
	        webcam.set_quality(90); // Qualidade do JPG (1 - 100)
	        webcam.set_shutter_sound(true); // Toca o som de câmera (o arquivo shutter.mp3, que vem com os "utilitários" da câmera, deve estar no diretório raíz do site)
	    </script>

        <!--Corpo da página-->
        <asp:UpdatePanel runat="server" ID="upd1">
            <ContentTemplate>
                <asp:HiddenField runat="server" ID="hdfContador" Value="0" /> <!--Este "contador" serve para gerar uma URL da foto diferente a cada captura, para atualizar o controle que a exibe.-->
                <asp:MultiView runat="server" ID="mv1" ActiveViewIndex="0">
                    <asp:View runat="server" ID="vwPesquisa">
                        <table style="width:100%;">
                            <tr>
                                <td style="text-align:center;">
                                    <span>Pesquisa de Pessoas</span>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <span>Nome:</span><br />
                                    <asp:TextBox runat="server" ID="tbxPesqNome" Width="200px"></asp:TextBox>
                                    <asp:Button runat="server" ID="btnPesquisa" Text="Pesquisar" OnClick="btnPesquisa_Click" />
                                    <asp:Button runat="server" ID="btnNovo" Text="Novo" OnClick="btnNovo_Click" />
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <asp:GridView runat="server" ID="gvPesquisa" AutoGenerateColumns="false" DataKeyNames="Id" Width="100%">
                                        <Columns>
                                            <asp:TemplateField HeaderText="Nome">
                                                <ItemTemplate>
                                                    <asp:LinkButton runat="server" ID="lnkEdit" CommandArgument='<%#Bind("Id")%>' Text='<%#Bind("Nome")%>' OnClick="lnkEdit_Click"></asp:LinkButton>
                                                </ItemTemplate>
                                            </asp:TemplateField>
                                        </Columns>
                                    </asp:GridView>
                                </td>
                            </tr>
                        </table>
                    </asp:View>
                    <asp:View runat="server" ID="vwCadastro">
                        <table style="width:100%;border:1px solid black;">
                            <tr>
                                <td colspan="3" style="text-align:center;">
                                    <span>Cadastro de Pessoas com Foto</span>
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <span>Código:</span>
                                </td>
                                <td>
                                    <asp:TextBox runat="server" ID="tbxCodigo" Width="150px" ReadOnly="true"></asp:TextBox>
                                </td>
                                <td rowspan="2" style="text-align:center;">
	                                <!-- Desenha o HTML do Flash que faz a interface com a Webcam na resolução 320x240 -->
                                    <div id="upload_results" runat="server" style="width:320px;height:240px;background-color:Blue;">
                                        <asp:Image runat="server" ID="imgFoto" ImageUrl="~/SessionImg.aspx" AlternateText="Captura de Foto" />
                                    </div>
                                    <asp:Button runat="server" ID="btnAbrePopCaptura" Text="Capturar Imagem" OnClick="btnAbrePopCaptura_Click" />
                                </td>
                            </tr>
                            <tr>
                                <td>
                                    <span>Nome:</span>
                                </td>
                                <td colspan="2">
                                    <asp:TextBox runat="server" ID="tbxNome" Width="200px"></asp:TextBox>
                                </td>
                            </tr>
                            <tr>
                                <td colspan="3" style="text-align:center;">
                                    <asp:Button runat="server" ID="btnCancelar" Text="Cancelar" OnClick="btnCancelar_Click" />
                                    <asp:Button runat="server" ID="btnSalvar" Text="Salvar" OnClick="btnSalvar_Click" />
                                </td>
                            </tr>
                        </table>
                    </asp:View>
                </asp:MultiView>
                <asp:Button runat="server" ID="btnRefreshFoto" Style="display:none" OnClick="btnRefreshFoto_Click" />
            </ContentTemplate>
        </asp:UpdatePanel>

        <asp:ModalPopupExtender ID="popWebCam" runat="server" PopupControlID="pnlPopWebCam" CancelControlID="btnFechaPopWebCam" TargetControlID="btnAcionaPopWebCam" BehaviorID="bpopWebCam"></asp:ModalPopupExtender>
        <asp:Button runat="server" ID="btnAcionaPopWebCam" Style="display:none;" />
        <asp:Panel runat="server" ID="pnlPopWebCam" Width="320px" Height="280px" BackColor="Black">
            <table style="width:100%;" cellpadding="0" cellspacing="0">
                <tr>
                    <td style="width:320px;height:240px;text-align:center;" valign="top">
                        <script type="text/javascript" language="JavaScript">
                            document.write(webcam.get_html(320, 240));
	                    </script>
                        <input type="button" value="Configurar..." onclick="webcam.configure();" />
                        <input type="button" value="Capturar" onclick="take_snapshot();" />
                        <input type="button" value="Reset" onclick="webcam.reset();" />
                        <asp:Button runat="server" ID="btnFechaPopWebCam" Text="Fechar" />
                    </td>
                </tr>
            </table>
        </asp:Panel>

        <!--Funções para controle da WebCam-->
        <script type="text/javascript">
            webcam.set_hook('onComplete', 'my_completion_handler');

            function take_snapshot() {
                // Captura a imagem e submete ao servidor
                document.getElementById('upload_results').innerHTML = '<h1>Realizando Upload da Foto...</h1>';
                webcam.snap();
                document.getElementById('<%=btnFechaPopWebCam.ClientID%>').click();
            }

            function my_completion_handler(msg) {
                document.getElementById('<%=btnRefreshFoto.ClientID%>').click(); //Executa a rotina que atualiza o controle que exibe a foto com a foto que está na sessão.
            }
        </script>

    </form>
</body>
</html>

Vamos fazer a nossa página de cadastro em um “Single Page Module”, ou seja, a mesma página serve para pesquisa, inclusão e alteração. Temos um MultiView com duas visões: uma para pesquisa e outra para o cadastro em si.

A View de Pesquisa (vwPesquisa) não tem segredo: Possui um campo para pesquisar por nome, os botões Pesquisar e Novo e um gridview para mostrar os resultados da pesquisa.

Na View de Cadastro (vwCadastro) temos os campos necessários de código, nome, os botões para salvar e cancelar e um componente do tipo Image. Neste componente é que vamos carregar a foto que estará armazenada em nossa variável de sessão (SessionFacade.FotoArmazenada). Também temos, abaixo do Image, um botão que faz a captura da foto.

Notou que temos em nossa solution um arquivo chamado SessionImg.aspx e que está na propriedade ImageUrl do controle imgFoto? Pois é. Colocaremos no Stream de saída de SessionImg.aspx a imagem de nossa variável SessionFacade.FotoArmazenada. Portanto, esta página será interpretada pelo navegador como um arquivo de imagem. Veja como isso é feito:

protected void Page_Load(object sender, EventArgs e)
{
	Response.Clear();
	if (SessionFacade.FotoArmazenada != null)
	{
		Response.BinaryWrite(SessionFacade.FotoArmazenada);
	}
	else
	{
		Response.Write("Foto não disponível");
	}
	Response.End();
}

Também moveremos a parte que contém o applet Flash para a captura de foto para um Panel, e este com um extender ModalPopupExtender. Assim, ao clicarmos no botão “'Capturar Imagem” da tela de cadastro abriremos este Panel em uma janela modal e a imagem estará pronta para ser capturada.

Clicando no botão “Capturar” do modal da WebCam, dispararemos a função JavaScript “take_snapshot()”, do jeito que fazíamos antes, mas esta função tem uma pequena diferença: ela dispara uma rotina server-side invocada pelo botão “btnFechaPopWebCam”, que nada mais faz do que invocar o método Hide() do ModalPopupExtender (isto poderia ser feito client-side, mas não lembro como faz :( ).

O handler JavaScript que ouve o evento que é disparado quando o processamento estiver concluído também foi alterado. No lugar de exibir a URL gravada fisicamente no servidor, ele vai disparar uma rotina server-side que está no botão “btnRefreshFoto”. Antes de entrar no que ela faz, notaram que no HTML, logo após a declaração do ContentTemplate do UpdatePanel temos um HiddenField chamado “hdfContador”? Ele vai servir para uma POG que vou explicar abaixo:

protected void btnRefreshFoto_Click(object sender, EventArgs e)
{
	hdfContador.Value = (Int32.Parse(hdfContador.Value) + 1).ToString(); 
	imgFoto.ImageUrl = "SessionImg.aspx?c=" + hdfContador.Value;
	imgFoto.AlternateText = "Foto";
}

Para exibir corretamente as alterações do componente Image, devemos modificar a sua propriedade ImageURL. Prestaram atenção? eu disse MODIFICAR a propriedade. Como temos uma única página que exibe o conteúdo da variável de sessão (SessionImg.aspx), para que a URL possa ser modificada vamos alterar apenas a QueryString da mesma. A cada vez que precisamos mudar a foto, incrementamos esse contador e concatenamo-os com a URL SessionImg.aspx. Com a QueryString modificada, o navegador entende que é um outro link e redesenha a área do Image.

Acima somente trabalhamos na interface Web do projeto. Agora vamos tratar de persistir esses dados em um Banco de Dados.

A persistência é a parte mais simples. Temos uma tabela em nosso banco chamada Cadastro, com as definições abaixo:

create table Cadastro (
	Id int not null identity,
	Nome varchar(100),
	Foto image
);
alter table Cadastro add constraint PK_CADASTRO primary key (Id);

Para persistir esta tabela, criamos a classe PessoaVO para representar os objetos e PessoaDAL para executar os comandos de gravação e pesquisa. PessoaDAL executa os métodos comumentes usados para persistir dados. Suponho que você já saiba utilizar o ADO.NET e métodos relacionados (o código completo está no download do exemplo, não se preocupe ;) ).

Vamos ver o código de gravação de dados de PessoaDAL:

        public static string Gravar(PessoaVO arg)
        {
            try
            {
                using (SqlConnection cnx = new SqlConnection(ConfigurationManager.ConnectionStrings["cnx"].ToString()))
                {
                    using (SqlCommand cmd = new SqlCommand())
                    {
                        cnx.Open();
                        cmd.Connection = cnx;

                        StringBuilder sql = new StringBuilder();

                        if (arg.Id == 0) //Insert
                        {
                            sql.Append("insert into Cadastro (Nome,Foto) values (@Nome,@Foto)");
                            cmd.Parameters.Add("@Nome", SqlDbType.VarChar, 100).Value = arg.Nome;
                            cmd.Parameters.Add("@Foto", SqlDbType.Image).Value = (arg.Foto == null) ? (object)DBNull.Value : arg.Foto;
                        }
                        else //Update
                        {
                            sql.Append("update Cadastro set Nome = @Nome, Foto = @Foto where Id = @Id");
                            cmd.Parameters.Add("@Nome", SqlDbType.VarChar, 100).Value = arg.Nome;
                            cmd.Parameters.Add("@Foto", SqlDbType.Image).Value = (arg.Foto == null) ? (object)DBNull.Value : arg.Foto;
                            cmd.Parameters.Add("@Id", SqlDbType.Int).Value = arg.Id;
                        }
                        cmd.CommandText = sql.ToString();
                        cmd.ExecuteNonQuery();
                    }
                }

                return String.Empty;
            }
            catch (Exception ex)
            {
                return ex.Message;
            }
        }

Para transferir os dados da página para o objeto PessoaVO, temos o seguinte código do botão Salvar:

protected void btnSalvar_Click(object sender, EventArgs e)
{
	PessoaVO p = new PessoaVO();
	p.Id = tbxCodigo.Text.Equals("") ? 0 : Int32.Parse(tbxCodigo.Text);
	p.Nome = tbxNome.Text;
	p.Foto = SessionFacade.FotoArmazenada;

	string ret = PessoaDAL.Gravar(p);
	if (ret == "")
	{
		ScriptManager.RegisterStartupScript(this, this.GetType(), "alert", "alert('Pessoa gravada com sucesso');", true);
		mv1.SetActiveView(vwPesquisa);
	}
	else
	{
		ScriptManager.RegisterStartupScript(this, this.GetType(), "alert", "alert('Erro ao gravar Pessoa');", true);
	}
}

Atribuímos os campos normalmente, sendo que no campo “Foto” colocamos os dados gravados em nossa variável de sessão SessionFacade.FotoArmazenada.

Para exibir um registro, recuperamos ele do BD e fazemos a atribuição das propriedades na caixa de texto. Quanto à foto, atribuimos ela na variável de sessão e fazemos aquela “gambiarra” para atualizar o componente Image.

Bem, é isso aí. Sei que o design do exemplo está tosco, mas o que interessa é a gravação do registro em BD, e você viu que é bem simples. Um abraço e até a próxima!

Projeto ASP.NET com captura de imagem via webcam e gravação em Banco de Dados (básico) (.NET Framework 4.0 em Visual Studio 2010).