ASP.net Core APP 06 – Armazenando imagens
O Azure Storage é um serviço que permite o armazenamento de grandes volumes de dados, ele trabalha com alguns modelos de diferentes de armazenamento:
- Contêineres – que armazenam qualquer tipo de arquivo no formato de objetos
- Sistema de arquivos compartilhados
- Tabelas – no formato relacional
- Filas – conjunto de mensagens em formato de objetos JSON
Para maiores detalhes a respeito da integração de aplicações com Azure Storage veja a documentação oficial da Microsoft em: https://docs.microsoft.com/pt-br/azure/storage/blobs/storage-quickstart-blobs-dotnet#upload-blobs-to-a-container
O primeiro passo para fazer nossa aplicação encaminhar as mensagens para o Azure Storage é acessar sua conta do Azure e no menu principal clicar na opção criar recurso.

Na próxima tela, no campo de busca digite Armazenamento e selecione a opção Conta de armazenamento.

Clique no botão Criar.

Selecione a sua assinatura, e um grupo de recurso.

Na parte inferior desta mesma tela, informe os seguintes parâmetros:
– Nome da conta de armazenamento: storagefaegaspnetcoreSEUNOME
– Tipo da conta StorageV2
– Replicação: LRS
Então clique em avançar

Na próxima etapa mantenha a opção Ponto de extremidade público, para permitir que possamos conectar no Storage de fora do Azure. Clique em avançar.

Na próxima etapa mantenha as configurações padrões e clique em Avançar.

Avance até a última tela de confirmação das configurações e clique no botão Criar.

Aguarde alguns instantes até a conclusão da criação do recurso e clique no botão ir para o recurso.

Nesta tela inicial temos uma visão geral do recurso de Storage que foi criado. Clique no link Contêineres no centro desta página.

Na próxima tela, clique no botão +Contêineres.

Na próxima tela, informe o nome do novo contêiner como: shoppingfaeg clique no botão OK para confirmar a criação.


Precisamos agora recuperar a string de conexão com o serviço, para isso selecione a opção Chave de Acesso. Mantenha essa janela aberta, em breve vamos retornar para copiar a string de conexão.

Agora vamos alterar o código da aplicação para conectar no Azure Storage e enviar os arquivos, mas para isso precisamos instalar uma biblioteca. Para isso na pasta principal da aplicação execute o comando:
dotnet add package Microsoft.Azure.Storage.Blob
Como primeira alteração na aplicação vamos criar uma nova interface no pacote de serviço chamada IStorageService, esse serviço será responsável por conectar ao Azure Storage e enviar o arquivo para armazenamento.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace myshop.Services
{
public interface IStorageService
{
Task<string> saveFile(IFormFile file);
}
}

Agora vamos criar a implementação da interface, para isso na mesma pasta de Services, crie uma classe chamada StorageService. Observe que no construtor temos uma variável que deve receber a string de conexão com o serviço do Azure Storage, em seguida vamos implementar o método saveFile(), que recebe a instância da class IFormFile, connecta no Azure Storage criando um contêiner chamado shoppingfaeg, e em seguida gera um nome dinâmico para o arquivo que será salvo impedindo a sobre escrita de arquivos com o mesmo nome. Por fim o arquivo é enviado para o Azure Storage e seu novo nome é retornado.
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Storage;
using Microsoft.Azure.Storage.Blob;
namespace myshop.Services
{
public class StorageService : IStorageService
{
private CloudStorageAccount storageAccount;
public StorageService( )
{
var storageConnectionString = "AQUIVAIASTRINGDECONEXAODOAZURESTORAGE";
CloudStorageAccount.TryParse(storageConnectionString, out storageAccount);
}
public async Task<string> saveFile(IFormFile file)
{
if(storageAccount != null){
CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
CloudBlobContainer cloudBlobContainer =
cloudBlobClient.GetContainerReference("shoppingfaeg");
await cloudBlobContainer.CreateIfNotExistsAsync();
string newName = System.Guid.NewGuid().ToString() + file.FileName;
var cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(newName);
await cloudBlockBlob.UploadFromStreamAsync(file.OpenReadStream());
return newName;
}
return string.Empty;
}
}
}

Em seguida precisamos registrar esse novo serviço na aplicação, para isso vamos alterar o código da classe Startup, para no método ConfigureServices inserir a seguinte linha para registrar o novo serviço.
services.AddScoped<IStorageService,StorageService>();

Agora podemos alterar o código do formulário do produto para incluir o campo para fazer o upload do arquivo da imagem do produto e em seguida, salvar o novo nome do arquivo que será armazenado no Azure Storage, para isso vamos modificar o código da classe Product do pacote Models, para inserir o atributo Photo e o atributo FileNameStorage. Observe que anotamos a propriedade Photo com o decorador BsonIgnore, com isso esse campo não será serializado para o banco de dados Mongo.
using Microsoft.AspNetCore.Http;
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
namespace myshop.Models
{
public class Product
{
[BsonId]
[BsonRepresentation(BsonType.ObjectId)]
public string Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public float Price { get; set; }
[BsonIgnore]
public IFormFile Photo { get; set; }
public string FileNameStorage { get; set; }
}
}

Agora vamos modificar o formulário do controlador Product, para permitir que o formulário envie dados e arquivos binários no mesmo POST para o servidor, para isso vamos alterar a declaração do nosso FORM.
@using (Html.BeginForm("Form","Product",FormMethod.Post,new { enctype="multipart/form-data"})){
Em seguida vamos incluir um novo conjunto de elementos HTML para desenhar o botão de seleção do arquivo para upload.
<div class="form-group">
@Html.LabelFor(m => m.Photo)
@Html.EditorFor(m => m.Photo)
@Html.ValidationMessageFor(m => m.Photo)
</div>

Agora precisamos modificar nossa classe ProductController para primeiro receber por injeção de dependência a instância do IStorageService.
private IStorageService storageService;
public ProductController(IProductService productService, IStorageService storageService)
{
this.productService = productService;
this.storageService = storageService;
}

Ainda nesta classe, vamos modificar o código do método Form, para chamar de forma assíncrona o método saveFile da classe StorageService. Importante observar que a assinatura do método Form foi modificada para permitir aguardar o envio do arquivo de forma assíncrona.
[HttpPost]
public async Task<IActionResult> Form(Product product){
if (ModelState.IsValid)
{
var fileName = await this.storageService.saveFile(product.Photo);
product.FileNameStorage = fileName;
this.productService.save(product);
return RedirectToAction("Index");
}
return View(product);
}

Agora coloque a aplicação em execução e em seguida, acesse a url: https://localhost:5001/Product/Form faça o cadastro de um novo produto selecionando um arquivo de imagem para o produto.

Após a conclusão da inserção do registro e o envio do arquivo para o Azure Storage, o registro será apresentado na tela inicial da aplicação.

E se você acessar o contêiner no Azure Storage, observará que o arquivo com o mesmo nome foi inserido com sucesso.
