segunda-feira, 9 de setembro de 2013

ASP.NET MVC 4 - Upload e Visualização de Imagens

Neste artigo veremos como o processo de manipulação de imagens no ASP.NET MVC se tornou mais simples. Neste tutorial, faremos o seguinte:
1 - Criaremos um projeto ASP.NET MVC 4 e adicionaremos um banco de dados local, via EntityFramework, com uma tabela para armazenar os dados da imagem na qual efetuaremos o Upload
2 - Atualizaremos a interface MVC para efetuar o Upload de um arquivo
3 - Criaremos um mecanismo para visualizar a imagem que foi inserida no nosso banco de dados local

1 - Projeto com a tabela de Imagens

Crie um novo projeto no VS 2012, conforme imagens abaixo:



Adicione uma classe chamada [Imagem.cs] dentro da pasta [Models] do projeto criado, com o seguinte conteúdo:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;

namespace MvcApplication2.Models
{
    public class Imagem
    {
        public static int _inc;
        public int Id { get; set; }
        public string Nome { get; set; }
        public string Conteudo { get; set; }
        public string Tipo { get; set; }
        public int Tamanho { get; set; }

        public Imagem() {
            Id = Interlocked.Increment(ref _inc);
        }
    }
}

Compile o projeto, e depois adicione uma classe controladora chamada [ImagemController] dentro da pasta [Controllers] do projeto, da forma que vemos nas imagens abaixo:



Neste ponto já temos nosso cadastro pronto. Vamos adicionar o objeto de Upload de imagens nas telas [Create.cshtml e Edit.cshtml].

2 - Atualização da Create.cshtml

Atualize conteúdo da View Create.cshtml por todo código abaixo:


@model MvcApplication2.Models.Imagem

@{
    ViewBag.Title = "Create";
}

<h2>Create</h2>

<script src="~/Scripts/jquery-1.8.2.js" type="text/javascript"></script>

<script type="text/javascript">

    $(document).ready(function () {
        $("#divFileUpload").mouseenter(function () {
            imageStatusOnHover();
        }).mouseleave(function(){ 
            imageStatusOffHover();
        });

    });

    function imageStatusOnHover() {
        if ($("#file1").val() != "") {
            var fileURL = $("#file1").val().replace(/\\/g, "\\\\");
            showImagemTooltip(fileURL);
        }
    }

    function imageStatusOffHover() {
        $('#divImageTooltip').hide();
    }

    function showImagemTooltip(file) {
        $('#divImageTooltip').css({
            "backgroundImage": "url(" + file + ")",
            "background-repeat": "no-repeat",
            "background-size": "contain",
            "background-position": "center",
            "background-color": "white"
        });

        $('#divImageTooltip').show();
        $("#divFileUpload").mousemove(function (event) {
            $('#divImageTooltip').css({ 'top': event.pageY, 'left': event.pageX });
        });
    }


</script>

@using (Html.BeginForm("Create", "Imagem", FormMethod.Post, new { enctype = "multipart/form-data" })) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Imagem</legend>

        <table>
            <tr>
                <td>
                    <div id="divFileUpload">
                        <input type="file" id="file1" name="file1" />
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <div id="divImageTooltip" style="display:none; position: absolute; height:200px; width:200px; border:solid 1px black;"></div>
                </td>                
            </tr>
        </table>

        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

Desta forma, executando a aplicação, adicionando a URL "/Imagem" e clicando no botão [Create New], poderá já testar o recurso de Upload e Visualização de uma imagem [ao passar o mouse sobre o objeto de Upload], conforme mostra a tela abaixo:



Agora precisamos criar um processo para salvar/visualizar a imagem no nosso banco de dados.

3 - Salvar e Visualizar a Imagem do Banco de Dados

Altere todo conteúdo do seu arquivo [Index.cshtml] pelo especificado abaixo:


@model IEnumerable<MvcApplication2.Models.Imagem>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.Nome)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Tipo)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Tamanho)
        </th>
        <th></th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.DisplayFor(modelItem => item.Nome)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Tipo)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Tamanho)
        </td>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.Id }) |
            @Html.ActionLink("Details", "Details", new { id=item.Id }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.Id })
        </td>
    </tr>
}

</table>


Altere todo o conteúdo do arquivo [Edit.cshtml] pelo conteúdo abaixo:


@model MvcApplication2.Models.Imagem

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

<script src="~/Scripts/jquery-1.8.2.js" type="text/javascript"></script>

<script type="text/javascript">

    $(document).ready(function () {
        $("#divFileUpload").mouseenter(function () {
            imageStatusOnHover();
        }).mouseleave(function(){ 
            imageStatusOffHover();
        });

    });

    function imageStatusOnHover() {
        var fileTitleContent = $("#fileTitle1")[0].textContent;
        if (fileTitleContent != "")
        {
            var url = String(window.location.protocol) + "//" + String(window.location.host) + "/";
            url += "Imagem/BuscarAnexoImagemPorNome?nomeImagem=" + fileTitleContent;

            $.ajax({
                type: "POST",
                url: url,
                success: function (file) {
                    showImagemTooltip(file);
                }
            });
        }
    }

    function imageStatusOffHover() {
        $('#divImageTooltip').hide();
    }

    function showImagemTooltip(file) {
        $('#divImageTooltip').css({
            "backgroundImage": "url(" + file.Url + ")",
            "background-repeat": "no-repeat",
            "background-size": "contain",
            "background-position": "center",
            "background-color": "white"
        });

        $('#divImageTooltip').show();
        $("#divFileUpload").mousemove(function (event) {
            $('#divImageTooltip').css({ 'top': event.pageY, 'left': event.pageX });
        });
    }


</script>

@using (Html.BeginForm("Edit", "Imagem", FormMethod.Post, new { enctype = "multipart/form-data" }))
{

    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Imagem</legend>

        <table>
            <tr>
                <td>
                    <div id="divFileUpload">
                        <label id="fileTitle1">@ViewBag.FileTitleLabel</label>
                        <input type="file" id="file1" name="file1" />
                    </div>
                </td>
            </tr>
            <tr>
                <td>
                    <div id="divImageTooltip" style="display:none; position: absolute; height:200px; width:200px; border:solid 1px black;"></div>
                </td>                
            </tr>
        </table>

        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

<div>
    @Html.ActionLink("Back to List", "Index")
</div>

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}


E finalmente, altere também todo conteúdo da classe [ImagemController.cs] pelo especificado abaixo.
Obs: Como o objetivo é mostrar apenas os recursos de imagem, não me preocupei em separar o projeto em camadas, pois vai notar que o acesso a dados ficou na classe controladora.


using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MvcApplication2.Models;

namespace MvcApplication2.Controllers
{
    public class ImagemController : Controller
    {
        private StoreDBContext db = new StoreDBContext();

        //
        // GET: /Imagem/

        public ActionResult Index()
        {
            return View(db.Imagems.ToList());
        }

        //
        // GET: /Imagem/Details/5

        public ActionResult Details(int id = 0)
        {
            Imagem imagem = db.Imagems.Find(id);
            if (imagem == null)
            {
                return HttpNotFound();
            }
            return View(imagem);
        }

        //
        // GET: /Imagem/Create

        public ActionResult Create()
        {
            return View();
        }

        //
        // POST: /Imagem/Create

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Create(Imagem imagem)
        {
            var img = new Imagem();
            var file1 = Request.Files["file1"];

            if (!string.IsNullOrEmpty(file1.FileName))
            {
                img.Nome = file1.FileName.Substring(file1.FileName.LastIndexOf("\\") + 1);

                var binaryData = new Byte[file1.InputStream.Length];
                file1.InputStream.Read(binaryData, 0, (int)file1.InputStream.Length);
                var base64String = Convert.ToBase64String(binaryData, 0, binaryData.Length);
                img.Conteudo = base64String;

                img.Tamanho = file1.ContentLength;
                img.Tipo = file1.ContentType;

                db.Imagems.Add(img);
                db.SaveChanges();
            }

            return RedirectToAction("Index");
        }

        //
        // GET: /Imagem/Edit/5

        public ActionResult Edit(int id = 0)
        {
            Imagem imagem = db.Imagems.Find(id);

            ViewBag.FileTitleLabel = "";
            if (imagem != null) ViewBag.FileTitleLabel = imagem.Nome;

            return View(imagem);
        }

        //
        // POST: /Imagem/Edit/5

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Edit(Imagem imagem)
        {

            ViewBag.FileTitleLabel = "";

            return RedirectToAction("Index");
        }

        //
        // GET: /Imagem/Delete/5

        public ActionResult Delete(int id = 0)
        {
            Imagem imagem = db.Imagems.Find(id);
            if (imagem == null)
            {
                return HttpNotFound();
            }
            return View(imagem);
        }

        //
        // POST: /Imagem/Delete/5

        [HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Imagem imagem = db.Imagems.Find(id);
            db.Imagems.Remove(imagem);
            db.SaveChanges();
            return RedirectToAction("Index");
        }

        public ActionResult BuscarAnexoImagemPorNome(string nomeImagem)
        {
            return Json(new { Url = Url.Action("CriarAnexoImagem", "Imagem", new { nomeImagem = nomeImagem }) });
        }

        public FileContentResult CriarAnexoImagem(string nomeImagem)
        {
            var file = db.Imagems.Where(w => w.Nome.Equals(nomeImagem));
            if (file == null) return null;

            return File(Convert.FromBase64String(file.FirstOrDefault().Conteudo), file.FirstOrDefault().Tipo, file.FirstOrDefault().Nome);
        }

        protected override void Dispose(bool disposing)
        {
            db.Dispose();
            base.Dispose(disposing);
        }
    }
}


Nesta classe controladora, e no arquivo Edit.cshtml, vai notar pontos interessantes:
- Ao passar o mouse sobre o objeto de Upload de arquivos, ocorre uma chamada Ajax para executar o método [Imagem/BuscarAnexoImagemPorNome]
- Este método busca o registro da imagem e retorna um JSON com a chamada para outro método [CriarAnexoImagem] que vai retornar o arquivo (FileContentResult), através da conversão da String que foi salva no formato [Base64] no banco de dados (na Action Create)
- Depois o JScript [showImagemTooltip] mostra a imagem em um Div, tendo como background a imagem retornada.

Em fim, sua tela de consulta deverá estar como exemplificado abaixo:



[]s

2 comentários:

  1. FIZ TODO O TUTORIAL, MAS NO CODIGO DO CONTROLLER ESTE UE ESTA NO FINAL ESTÁ DANDO ERRO NO MEU NESTA LINHA "private StoreDBContext db = new StoreDBContext();" O StoreDBContext ESTÁ DANDO ERRO POR FAVOR ME AJUDE COM ESSE CODIGO. VISUAL STUDIO 2013.

    ResponderExcluir
  2. Olá,
    Note a [última] imagem do passo1, onde crio a classe [StoreDBContext].
    O que deu de errado neste passo para você?

    ResponderExcluir

<< Ao enviar um comentário, favor clicar na opção [Enviar por e-mail comentários de acompanhamento para gtezini@gmail.com] >>