Trabalhando com foto e Web Api usando CSharp

Olá pessoal, hoje eu vou falar um pouco de como trabalhar com foto e Web Api com a linguagem C# para web ou desktop. O primeiro ponto que gostaria de informar é que a melhor performance para desenvolver um aplicativo ou sistema que tenha foto é com esses dois próximos passos importantes.

Primeiro passo

Definir um servidor onde as fotos vão ficar. Isso porque é necessário deixar a foto em um servidor disponível para todos verem, porém não são todos que vão ver. Isso significa que nem todos os usuários podem acessar as fotos no servidor, só aquele que for dono da foto.

Existem vários servidores para hospedagem de fotos hoje em dia, alguns deles inclusive te fornece um endereço para fazer upload e download. Outros fornecem apenas API para que você desenvolva um sistema consumindo a API disponibilizada.

O que eu mais utilizo hoje em dia é o AWS da Amazon e o Azure da Microsoft. Existe também o Cloudinary e muitos outros [1]. Ainda dentro do primeiro passo, é importante você definir o banco de dados que irá utilizar.

Segundo passo

Dentro do segundo passo, é importante definir em seu sistema quem vai fazer o upload da imagem que no meu ponto de vista, se for um sistema web ele vai fazer antes de mandar para o banco de dados, se for mobile, ele que deve fazer o upload antes de mandar para o banco de dados.

Depois de fazer o upload da imagem, deve ser pego o nome da imagem gerada aleatoriamente e enviada para o Web API feito para gravar os dados do banco de dados. Mas apenas o nome da imagem ou então o endereço dela. O endereço completo por exemplo: https://endereco_do_site/pasta/imagem_29293.png.

Com o nome e endereço da imagem, ai sim é hora de gravar a imagem dentro do banco de dados. O sistema depois precisa renderizar o endereço como imagem passando o endereço completo. Para alguns casos, é melhor colocar apenas o nome da imagem dentro do banco de dados, isso porque caso o cliente mude de servidor, basta mudar a base da URL que tudo volta a funcionar mostrando a imagem.

Em diversas empresas que trabalhei, o erro estava:

  1. A imagem armazenada direta no banco de dados, deixa o banco lento para mostr√°-la;
  2. O nome da imagem armazenada direta no banco de dados com o endere√ßo completo do servidor, isso faz com que, depois do servidor cheio, √© necess√°rio armazenada em outro local, mas manter as imagens antigas no antigo servidor. O que fazer? √Č necess√°rio ter uma tabela no banco de dados informando os endere√ßos dos servidores, assim a tabela pode pegar de um endere√ßo ou de outro;
  3. Outro erro comum é ter a imagem fixa no código ou o endereço do servidor fixo no código sem qualquer tipo de comentário ou então com uma classe sem constantes.

Eu prefiro fazer da seguinte forma:

  1. Gerar o banco de dados para armazenar o nome da imagem;
  2. Gerar uma tabela apenas com o endereço do servidor ou servidores;
  3. Ao gravar o nome da imagem no banco, indicar como chave secundária qual o endereço do servidor pela tabela com endereços dos servidores.
  4. Deixar a parte web ou mobile fazer o upload da imagem dentro do servidor;
  5. Existem códigos prontos em PHP que basta chamar passando o endereço do arquivo para fazer upload da imagem pelo mobile com a imagem de forma binária.

Código Web API

Aqui eu vou mostrar como funciona o código de Web API feito na linguagem C# e usando a ferramenta Visual Studio da Microsoft.

Código 1.1 - Consultar usando rota com o id do aluno.

    [Route("api/Foto/Aluno/{IdAluno}")]
    [ResponseType(typeof(FotoTDB))]
    public IEnumerable<FotoTDB> GetFotoByAluno(Int32 idAluno)
    {
        StringBuilder str = new StringBuilder();
        str.Append(@"SELECT
                        IdFoto, IdTipoFoto, IdAluno, NomeFoto
                    FROM
                        FotoTDB
                    WHERE IdAluno = @IdAluno");

        IDataParameter idStudent = new SqlParameter();
        idStudent.DbType = DbType.String;
        idStudent.ParameterName = "@IdAluno";
        idStudent.Value = idAluno;
        idStudent.SourceColumn = "IdAluno";

        var resultado = db.Database.SqlQuery<FotoTDB>(str.ToString(),
            idStudent).AsEnumerable();

        if (resultado == null)
            return null;

        return resultado;
    }

O c√≥digo 1.1 mostra primeiro a rota criada e que tamb√©m necessita que seja passado o Id do Aluno, note que o par√Ęmetro que ele precisa receber √© o mesmo que est√° no m√©todo, isto √©, o mesmo nome digitado na rota √© o mesmo que recebe como par√Ęmetro.

Logo depois, fa√ßo um SELECT com os campos da tabela de banco de dados e com a condi√ß√£o WHERE passando o IdAluno como par√Ęmetro. Em seguida, utilizei o IDataParameter para definir o par√Ęmetro com o valor recebido. Para finalizar, utilizei o SqlQuery para executar os valores e retornar todos os campos do SELECT.

O código 1.2 mostra como fazer o POST dos dados, isto é, o INSERT de valores dentro do banco de dados através de uma classe e utilizando um envio mais seguro do que o GET.

Código 1.2 - Fazendo Post dos dados

	// POST: api/Foto
    [ResponseType(typeof(FotoTDB))]
    public IHttpActionResult PostFotoTDB(FotoTDB fotoTDB)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        db.FotoTDB.Add(fotoTDB);
        db.SaveChanges();

        return CreatedAtRoute("DefaultApi", new { id = fotoTDB.idFoto }, fotoTDB);
    }

Note que o c√≥digo 1.2 n√£o tem uma rota criada por mim utilizando a tag ROUTE como mostrado no c√≥digo 1.1. Isso porque o m√©todo √© feito via POST e recebe como par√Ęmetro uma classe toda de dados chamada FotoTDB. Os dados s√£o verificados se s√£o v√°lidos e se forem, √© feito um Add com a classe toda.

Para finalizar, basta salvar as mudanças usando o método do EntityFramework chamado SaveChanges(). No final do método, basta criar uma nova rota passando o valor do id da foto criada.

Já o código 1.3 mostra como deletar do banco de dados um dado importante pelo Id. Para isso basta fazer um GET normal.

Código 1.3 - Deletando a foto do banco de dados

	// DELETE: api/Foto/5
    [ResponseType(typeof(FotoTDB))]
    public IHttpActionResult DeleteFotoTDB(long id)
    {
        FotoTDB fotoTDB = db.FotoTDB.Find(id);
        if (fotoTDB == null)
        {
            return NotFound();
        }

        db.FotoTDB.Remove(fotoTDB);
        db.SaveChanges();

        return Ok(fotoTDB);
    }

No meu ponto de vista, todo e qualquer tipo de informação hoje em dia não precisa ser deletada, basta desativar. Se você é o dono do banco de dados, quanto mais informação nele, melhor é para o seu negócio.

Para este caso eu mudaria o m√©todo de Remove para Update e SaveChanges() mudando apenas um status de ativo para inativo. Mesmo assim, o id da foto √© passada pelo m√©todo como recebimento de par√Ęmetro e depois disso √© verificado se existe esse id no banco de dados, se existir, ent√£o os dados ser√£o removidos.

No final do método, o MVC retorna um valor de Ok com a classe de fotoTDB todo preenchido.

O código 1.4 mostra como é feito a atualização dentro da API mudando apenas um status.

Código 1.4 - Atualizando os dados dentro do banco de dados.

	// PUT: api/Foto/5
    [ResponseType(typeof(void))]
    public IHttpActionResult PutFotoTDB(long id, FotoTDB fotoTDB)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != fotoTDB.idFoto)
        {
            return BadRequest();
        }

        db.Entry(fotoTDB).State = EntityState.Modified;

        try
        {
            db.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {
            if (!FotoTDBExists(id))
            {
                return NotFound();
            }
            else
            {
                throw;
            }
        }

        return StatusCode(HttpStatusCode.NoContent);
    }

Note que o nome do m√©todo do c√≥digo 1.4 come√ßa com a palavra Put, isso significa que o m√©todo √© respons√°vel por atualizar as informa√ß√Ķes do banco de dados. O nome do m√©todo j√° diz muita coisa.

O primeiro passo que o m√©todo faz √© verificar se os dados enviados s√£o v√°lidos, se forem ent√£o continua, sen√£o, retorna um BadRequest. Depois √© verificado se o id da foto foi enviado como par√Ęmetro separado, note que o esse m√©todo recebe dois tipos de par√Ęmetros. O primeiro √© o id e o segundo √© a classe preenchida com todos os novos valores.

Com os dados em perfeita situa√ß√£o, o State da classe √© modificada com o par√Ęmetro EntityState.Modified. Dentro do bloco try os dados do banco s√£o salvos com o comando SaveChanges(). Em caso de erro, existe o bloco catch que pode pegar o erro usando o comando DbUpdateConcurrencyException.

No final do método existe o StatusCode retornado para o programa que chamou.

Caso voc√™ queira saber mais e entender mais como funciona o EntityFramework e o Web API, acesse a refer√™ncia [2] para maiores informa√ß√Ķes. Espero que tenha gostado e qualquer d√ļvida, pode entrar em contato pelo site www.mauriciojunior.org.

Referências

[1] - Hospedagem de imagens gratuitamente, Autor PHILIPE KLING DAVID, Data de criação: 10 DE JUNHO DE 2008, Data de acesso: 19 de Dezembro de 2017.

[2] - Entity Framework e Web API - Criando e consumindo Web API, Autor Mauricio Junior, Data de acesso: 20 de Dezembro de 2017.