Utilizando o Azure Storage
Neste artigo, o autor mostra como armazenar dados estruturados e não estruturados utilizando as funcionalidades do Azure Storage, Blob e Tables.
Introdução
Esse artigo tem como objetivo demonstrar de maneira prática, como armazenar dados estruturados e não estruturados utilizando o Blob e o Tables, ambas funcionalidades do Azure Storage.
Porém, antes de colocarmos a mão na massa, vamos entender de maneira breve o que é cada uma dessas funcionalidades.
Blob File
O Blob é um serviço geralmente utilizado para armazenar grandes quantidades de dados não estruturados, tais como texto ou binários que podem ser acessados através de uma requisição HTTP ou HTTPS.
Tables
Tables é o serviço que armazena dados NoSQL na nuvem de maneira estruturada, fornecendo um repositório chave/atributo. Vale lembrar que as tables não possuem schema.
Bom, agora sem mais delongas, vamos para a parte prática.
Criando nosso Storage no Azure
Nessa primeira parte, vamos acessar o portal do Microsoft Azure através do link: https://portal.azure.com/ e efetuar o nosso login.
Feito isso, vamos utilizar a busca do Azure para achar o serviço Storage Account, conforme nos mostra a figura seguir e selecioná-lo.

Agora, vamos preencher o Name e o Resource Group (caso já possua um, não é necessário criar outro) para criar nossa Storage Account, conforme nos mostra a figura abaixo:

Depois de finalizado o processo de criação da nossa Storage Account, clique sobre ela e em seguida, clique na opção Access Keys que fica logo abaixo de Settings. Feito isso, copie sua Connection String clicando no botão ao lado, conforme a seguir.

Agora vamos começar a segunda parte, que é no Visual Studio.

Criando o Projeto
Vamos criar um projeto do tipo ASP.NET Core Web.API em branco, conforme a figura acima.
Definindo as configurações do Projeto
Clique sobre o projeto que acabamos de criar e clique em propriedades (Windows) ou Options (Mac), acesse as configurações de execução Default e crie uma nova variável de ambiente – que eu vou chamar de CONNECTION_AZURE – e como valor, vamos passar aquela Connection String que copiamos do nosso ambiente Azure passos atrás, conforme mostra a próxima figura.

Depois, na aba ASP.NET Core vamos definir nossa url padrão como index.html, e em seguida vamos clicar em OK.
Agora vamos voltar a nossa solution, criar uma pasta chamada Entities e dentro dela vamos criar uma classe chamada Pessoa, com o seguinte código:
using Microsoft.WindowsAzure.Storage.Table;
namespace Imasters.Entities
{
public class Pessoa : TableEntity
{
public Pessoa() { }
public Pessoa(string nome, string email)
{
this.PartitionKey = nome;
this.RowKey = email;
}
public string FotoUrl { get; set; }
}
}
Observe que precisamos herdar a classe TableEntity para mapearmos os objetos para o C#, além disso criamos um construtor personalizado usando nome e e-mail como chave e atributo para que sejam únicos na tabela.
Agora vamos criar uma pasta chamada Interfaces, e dentro dela criar a interface IImasterInterface.cs com o código abaixo:
using System.Collections.Generic;
using System.Threading.Tasks;
using Imasters.Entities;
using Microsoft.AspNetCore.Http;
namespace Imasters.Interfaces
{
public interface IImasterInterface
{
Task<string> UploadBlobFileAsync(IFormFile file);
Task Save(string name, string email, string documentUrl);
Task<List<Pessoa>> List();
}
}
No código acima criamos os métodos de Upload, Salvar e Listar que serão implementados logo abaixo pelo nosso serviço.
No serviço, primeiramente vamos criar uma pasta Services e em seguida vamos criar uma classe chamada ImasterService.cs com o seguinte código (comentários no próprio código para melhor entendimento):
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Imasters.Entities;
using Imasters.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Table;
namespace Imasters.Services
{
public class ImasterService : IImasterInterface
{
/// <summary>
/// Uploads the BLOB file async.
/// </summary>
/// <returns>Metódo que efetua o upload da foto da pessoa no Blob Storage.</returns>
/// <param name="file">File.</param>
public async Task<string> UploadBlobFileAsync(IFormFile file)
{
//Recupera a Connection String da variável de ambiente que definimos no inicio do projeto.
var storageAccount = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("CONNECTION_AZURE"));
//Variável que representa o endpoint do blob na Storage Account.
var blobClient = storageAccount.CreateCloudBlobClient();
//Tenta recuperar o container em que será salvo o Blob, se não existir ele cria. Obs: O nome do container não deve conter letras maisucúlas.
var container = blobClient.GetContainerReference("meucontainer");
await container.CreateIfNotExistsAsync();
//Recupera a referência do endereço do blob e faz o upload do mesmo.
var blockBlob = container.GetBlockBlobReference(file.FileName);
using (var fileStream = file.OpenReadStream())
{
await blockBlob.UploadFromStreamAsync(fileStream);
}
return blockBlob.Uri.ToString();
}
/// <summary>
/// Save the specified name, email and documentUrl.
/// </summary>
/// <returns>Método que salva os dados cadastrados nas tabelas do Storage do Azure.</returns>
/// <param name="name">Name.</param>
/// <param name="email">Email.</param>
/// <param name="documentUrl">Document URL.</param>
public async Task Save(string name, string email, string documentUrl)
{
//Recupera a Connection String da variável de ambiente que definimos no inicio do projeto.
var storageAccount = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("CONNECTION_AZURE"));
//Cria o client para a tabela.
var tableClient = storageAccount.CreateCloudTableClient();
//Tenta recuperar o objeto que representará a tabela pessoa, se não existir ele cria. Obs: O nome da tabela não deve conter letras maisucúlas.
var table = tableClient.GetTableReference("pessoa");
await table.CreateIfNotExistsAsync();
//Cria o objeto pessoa.
var person = new Pessoa(name, email);
person.FotoUrl = documentUrl;
//Efetua a inserção na tabela.
var tableOperation = TableOperation.Insert(person);
await table.ExecuteAsync(tableOperation);
}
/// <summary>
/// Metódo que irá listar todos os cadastros efetuados.
/// </summary>
/// <returns>The list.</returns>
public async Task<List<Pessoa>> List()
{
//Recupera a Connection String da variável de ambiente que definimos no inicio do projeto.
var storageAccount = CloudStorageAccount.Parse(Environment.GetEnvironmentVariable("CONNECTION_AZURE"));
//Cria o client para a tabela.
var tableClient = storageAccount.CreateCloudTableClient();
//Recupera o objeto que representará a tabela pessoa.
var table = tableClient.GetTableReference("pessoa");
//Inicializa a variável que receberá os dados.
var tableQuery = new TableQuery<Pessoa>();
//Inicializa a variavel que representa o token para null para garantir que se comece no inicio da tabela.
TableContinuationToken continuationToken = null;
//Inicializa a variável que receberá o resultado.
TableQuerySegment<Pessoa> tableQueryResult = null;
//Lê toda a tabela.
do
{
tableQueryResult = await table.ExecuteQuerySegmentedAsync(tableQuery, continuationToken);
continuationToken = tableQueryResult.ContinuationToken;
} while (continuationToken != null);
return tableQueryResult.Results;
}
}
}
Neste próximo passo, vamos criar um Controller que servirá para orquestrar nossas rotas, e para isso, na pasta Controllers já previamente criada na inicialização do projeto, crie o ImasterController.cs com o seguinte código:
using System.Collections.Generic;
using System.Threading.Tasks;
using Imasters.Entities;
using Imasters.Interfaces;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Imasters.Controllers
{
[Route("api/[controller]")]
public class ImasterController : Controller
{
private IImasterInterface _imasterService;
public ImasterController(IImasterInterface imasterService)
{
_imasterService = imasterService;
}
[HttpPost]
public async Task<IActionResult> Post(string name, string email, IFormFile file)
{
var url = await _imasterService.UploadBlobFileAsync(file);
await _imasterService.Save(name, email, url);
return Redirect("../index.html");
}
[HttpGet]
public async Task<List<Pessoa>> Get()
{
var people = await _imasterService.List();
return people;
}
}
}
Como vocês podem observar o código acima, ele é bem simples e apenas chama os métodos previamente implementados no ImasterService.cs.
Agora, para finalizar a parte “backend” do projeto vamos adicionar a injeção de dependência e habilitar nosso projeto para ler arquivos estáticos (CSS, Imagens e etc). Para isso, no arquivo Startup.cs no método ConfigureServices adicione a seguinte linha:
services.AddScoped<IImasterInterface, ImasterService>();
E no método Configure adicione as seguintes linhas:
app.UseDefaultFiles(); app.UseStaticFiles();
Agora finalmente finalizamos todo o nosso “backend”, faltando apenas uma página HTML para o formulário e outra página HTML para a listagem dos dados que seguem abaixo:
Index.html
<form class="form-labels-on-top" method="post" enctype="multipart/form-data" action="api/imaster">
<div class="form-title-row">
<h1>Cadastro de Pessoas</h1>
</div>
<div class="form-row">
<label>
<span>Nome</span>
<input type="text" name="name">
</label>
</div>
<div class="form-row">
<label>
<span>Email</span>
<input type="email" name="email">
</label>
</div>
<div class="form-row">
<label>
<span>Foto</span>
<input type="file" name="file" />
</label>
</div>
<div class="form-row">
<button type="submit">Salvar</button>
</div>
</form>
List.html
<div class="main-content">
<div class="list">
<div class="row list-title-row">
<div class="col-md-4"><strong>Nome</strong></div>
<div class="col-md-4"><strong>Email</strong></div>
<div class="col-md-4"><strong>Documento</strong></div>
</div>
<div id="result"></div>
</div>
</div>
<script>
$(document).ready(function () {
var uri = 'api/imaster';
$(document).ready(function () {
$.getJSON(uri).done(function (data) {
$.each(data, function (key, value) {
var valido;
if (value.isValid)
valido = "Sim";
else
valido = "Não";
$("#result").append("<div class='row list-row'>");
$("#result").append("<div class='col-md-3'>" + value.partitionKey + "</div>");
$("#result").append("<div class='col-md-3'>" + value.rowKey + "</div>");
$("#result").append("<div class='col-md-4'><img src='" + value.fotoUrl +"' width='120' style='margin-top: -30px;' /></div>");
$("#result").append("</div>");
});
});
});
});
</script>
Agora é só rodar o projeto e efetuar um cadastro. Porém, você verá que a imagem não vai aparecer e acontecerá um erro de permissão. Isso porque, por padrão, qualquer container criado é Private, portanto você deve alterar isso para Public para que você veja a imagem corretamente conforme mostra na figura abaixo:
Obviamente o Azure fornece opções de segurança para controlarmos acesso de nossos arquivos dentro de um Storage, mas acho que esse assunto fica para um próximo artigo.
Espero que tenham gostado e até a próxima!








