Xamarin Forms - Usando o Firebase Storage
 Neste artigo vamos continuar mostrando como usar o Firebase Storage em uma aplicação Xamarin Forms.

Continuando a primeira parte do artigo vamos criar agora o projeto Xamarin Forms no VS 2019 Community.

Criando o projeto Xamarin Forms

Vamos criar um novo projeto Xamarin Forms no VS 2019 Community com o nome XF_FirebaseStorage.

Nota: Verifique se você precisa atualizar as versões do Xamarin Forms e do Xamarin.Essentials

Você pode ver como criar um projeto no Xamarin Forms neste link: Criar Projeto Xamarin Forms

A seguir vamos incluir os seguinte pacotes no projeto criado:

  1. FirebaseStorage.net

  1. Xam.Plugin.Media

Este plugin necessita que a versão do seu Xamarin Forms seja a versão 4.7.0 ou superior e que o Xamarin.Essentials esteja atualizado para a última versão estável.

Também será necessário realizar as configurações exibidas pelo arquivo readme.txt as quais são:

1- No projeto Android:

a) Incluir no arquivo AssemblyInfo.cs as permissões abaixo:

[assembly: UsesFeature("android.hardware.camera", Required = false)]
[assembly: UsesFeature("android.hardware.camera.autofocus", Required = false)]

b) Incluir no arquivo AndroidManifest.xml o código a seguir:

<provider android:name="android.support.v4.content.FileProvider"
     android:authorities="${applicationId}.fileprovider"
     android:exported="false"
     android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"  android:resource="@xml/file_paths"></meta-data>
</provider>

E também as permissões para Camera e Internet. Dessa forma o arquivo AndroidManifest.xml deve ficar assim:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.xf_firebasestorage" android:installLocation="auto">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />

<application android:label="XamarinFirebase.Android">
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.companyname.XamarinFirebase.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" />
</provider>
</application>
</manifest>

Obs: A tag <provider> tem que estar dentro da tag <application>

c) Crie uma nova pasta chamada xml na pasta Resources;

d) Na pasta xml crie o arquivo file_paths.xml com o  código abaixo:

<?xml version="1.0" encoding="utf-8" ?>
     <paths xmlns:android="http://schemas.android.com/apk/res/android">
     <external-files-path name="my_images" path="Pictures" />
     <external-files-path name="my_movies" path="Movies" />
</paths>

Nota: Ao criar o arquivo file_paths.xml verifique se a build action esta definida para AndroidResource.

Para mais detalhes acesse o link: https://developer.android.com/training/camera/photobasics.html

Criando a página principal do projeto

A seguir vamos criar a página principal da aplicação usando o arquivo MainPage.xaml do projeto e incluindo o código abaixo:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="XF_FirebaseStorage.MainPage">
    <StackLayout>
        <StackLayout>
            <StackLayout HorizontalOptions="Center" VerticalOptions="Start">

                <Image Margin="0,0,0,10" HeightRequest="100" Source="firebase.png" ></Image>
                <Label Margin="0,0,0,10" Text="Firebase Storage" FontAttributes="Bold" 
                       FontSize="Large" TextColor="Gray" HorizontalTextAlignment="Center" />
                <Image x:Name="imagemSelecionada" HeightRequest="150"></Image>
                <Entry x:Name="txtNomeArquivo" Placeholder="Nome do arquivo"></Entry>

                <StackLayout  HorizontalOptions="CenterAndExpand" Orientation="Horizontal">
                    <Button x:Name="btnPega" WidthRequest="200" Text="Pegar Imagem" 
                                Clicked="BtnSelecionaImagem_Clicked"/>
                    <Button x:Name="btnUpload" WidthRequest="200" Text="Enviar Imagem" 
                                Clicked="BtnUpload_Clicked" />
                </StackLayout>

                <StackLayout HorizontalOptions="CenterAndExpand" Orientation="Horizontal">
                    <Button x:Name="btnDownload" WidthRequest="200" Text="Download" 
                                  Clicked="btnDownload_Clicked" />
                    <Button x:Name="btnDeleta" WidthRequest="200" Text="Deletar Imagem" 
                                  Clicked="btnDeleta_Clicked" />
                </StackLayout>

                <Label x:Name="lblPath"></Label>
            </StackLayout>
        </StackLayout>
    </StackLayout>
</ContentPage>

Abaixo temos a figura gerada pelo XAML Preview exibindo uma amostra da nossa página:

O fluxo de execução é o seguinte:

  1. Pegar Imagem - Primeiro você pode selecionar uma imagem a partir do dispositivo para enviar;
  2. Enviar Imagem - A seguir você envia a imagem para o Storage;
  3. Download - Você pode fazer o download da imagem do Storage pelo nome;
  4. Deletar Imagem - Pode também deletar a imagem do Storage pelo nome;

Agora vamos criar uma pasta Services no projeto e nesta pasta criar o arquivo FirebaseStorageService onde vamos definir os métodos usando a API do FirebaseStorage para realizar as operações para enviar, deletar e obter imagens.

Abaixo temos o código da classe FirebaseStorageService :

using Firebase.Storage;
using System;
using System.IO;
using System.Threading.Tasks;
namespace XF_FirebaseStorage.Services
{
    public class FirebaseStorageService
    {
        FirebaseStorage firebaseStorage = new FirebaseStorage("xamarinimag******o***");
        public async Task<string> UploadFile(Stream fileStream, string fileName)
        {
            try
            {
                var imageUrl = await firebaseStorage
                    .Child("Paisagens")
                    .Child(fileName)
                    .PutAsync(fileStream);
                return imageUrl;
            }
            catch(Exception ex)
            {
                throw ex;
            }
        }
        public async Task<string> GetFile(string fileName)
        {
            try
            {
                return await firebaseStorage
                    .Child("Paisagens")
                    .Child(fileName)
                    .GetDownloadUrlAsync();
            }
            catch(Exception)
            {
                throw;
            }
        }
        public async Task DeleteFile(string fileName)
        {
            try
            {
                await firebaseStorage
                     .Child("Paisagens")
                     .Child(fileName)
                     .DeleteAsync();
            }
            catch (Exception)
            {
                throw;
            }
        }
    }
}

Dois pontos importantes a destacar aqui:

1- Na instância da classe FirebaseStorage você deve usar a URI do seu projeto Firebase Storage;

2- Estamos definindo o nó Paisagens a partir do qual vamos incluir as imagens no Storage;

Para concluir vamos definir o código no arquivo MainPage.xaml.cs de cada um dos eventos Click dos botões de comando:

using Plugin.Media;
using Plugin.Media.Abstractions;
using System;
using System.ComponentModel;
using System.IO;
using Xamarin.Forms;
using XF_FirebaseStorage.Services;
namespace XF_FirebaseStorage
{
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        FirebaseStorageService fbStorageService = new FirebaseStorageService();
        MediaFile arquivoMediaFile;
        public MainPage()
        {
            InitializeComponent();
        }
        private async void BtnSelecionaImagem_Clicked(object sender, EventArgs e)
        {
            await CrossMedia.Current.Initialize();
            try
            {
                arquivoMediaFile = await Plugin.Media.CrossMedia.Current.PickPhotoAsync(
                        new Plugin.Media.Abstractions.PickMediaOptions
                {
                    PhotoSize = Plugin.Media.Abstractions.PhotoSize.Medium
                });
                
                if (arquivoMediaFile == null)
                    return;
                imagemSelecionada.Source = ImageSource.FromStream(() =>
                {
                    var imageStram = arquivoMediaFile.GetStream();
                    return imageStram;
                });
            }
            catch (Exception ex)
            {
                await DisplayAlert("Sucesso", ex.Message, "OK");
            }
        }
        private async void BtnUpload_Clicked(object sender, EventArgs e)
        {
            if (arquivoMediaFile == null)
            {
                await DisplayAlert("Erro", "Erro ao selecionar imagem.", "OK");
                return;
            }
            else
            {
                await fbStorageService.UploadFile(arquivoMediaFile.GetStream(), 
                              Path.GetFileName(arquivoMediaFile.Path));
                await DisplayAlert("Sucesso", "Imagem enviada ao Storage com sucesso.", "OK");
            }
        }
        private async void btnDownload_Clicked(object sender, EventArgs e)
        {
            string path = await fbStorageService.GetFile(txtNomeArquivo.Text);
            if (path != null)
            {
                lblPath.Text = path;
                await DisplayAlert("Sucesso", "Arquivo :  " + path + "  baixado", "OK");
            }
        }
        private async void btnDeleta_Clicked(object sender, EventArgs e)
        {
            await fbStorageService.DeleteFile(txtNomeArquivo.Text);
            lblPath.Text = string.Empty;
            await DisplayAlert("Sucesso", "Deletado", "OK");
        }
    }

Neste código estamos usando os métodos criados no serviço FirebaseStorageService para enviar e obter imagens.

Executando o projeto iremos obter o seguinte resultado:

Nota: Como estou usando o emulador não existem imagens na câmara.

A figura abaixo mostra como será visualizada a estrutura de dados no Storage:

Pegue o código do projeto compartilhado aqui :  XF_FirebaseStorage.zip

"Tendo sido, pois, justificados pela fé, temos paz com Deus, por nosso Senhor Jesus Cristo;"
Romanos 5:1

Referências:


José Carlos Macoratti