Advertisement
  1. Code
  2. Android SDK

Firebase para Android: Almacenamiento de Archivos

Scroll to top
Read Time: 10 min

Spanish (Español) translation by Nadia Castelli (you can also view the original English article)

Durante la conferencia Google I/O 2016, Firebase fue presentado ante la comunidad de desarrolladores como una gran herramienta que provee soporte back-end rápido para páginas web y aplicaciones móviles. Este tutorial presenta la funcionalidad de almacenamiento y recuperación de archivos disponible para tus aplicaciones Android.

Para aprender más acerca de otras funcionalidades de Firebase, como la base de datos en tiempo real, analytics, reporte de crashes y autenticación, echa un vistazo a algunos de nuestros otros tutoriales aquí en Envato Tuts+.

Configuración Inicial de la Autenticación

Para ser breves, nos saltearemos el proceso de configuración inicial general de Firebase en tu aplicación Android y en la consola de Firebase. Si esta es la primera vez que utilizas Firebase en una app, te sugiero que primero leas el artículo Comenzando con Firebase para Android, de Ashraff Hathibelagal.

Antes de que puedas comenzar a usar el Almacenamiento de Firebase, necesitarás o bien asegurarte de que tu usuario está autenticado, o cambiar las reglas de requerimiento de autenticación en la Consola de Firebase para permitir que usuarios sin autenticación accedan y suban archivos. Para hacerlo simple, haremos lo segundo. Comencemos yendo a la sección de Autenticación, seleccionando Storage en la columna de navegación izquierda. 

Firebase storage section navigationFirebase storage section navigationFirebase storage section navigation

Luego, verás que hay dos pestañas en la parte superior de la pantalla de Storage: Files (Archivos) y Rules (Reglas).

Rules tab in Firebase storageRules tab in Firebase storageRules tab in Firebase storage

Selecciona la pestaña Rules, y en la línea allow read, write: if request.auth != null;, reemplaza != por == y haz click en el botón PUBLISH (Publicar).

Authentication rules for Firebase storage accessAuthentication rules for Firebase storage accessAuthentication rules for Firebase storage access

Ahora, cualquier usuario de tu aplicación debería poder subir o descargar archivos de tu back-end de Firebase. Esto no es ideal para un ambiente productivo, pero hará que aprender acerca del Almacenamiento de Firebase sea mucho más sencillo sin tener que profundizar en el código de la autenticación.

Subida Manual de Archivos

Poder subir archivos desde una app es genial, pero a veces querrás simplemente almacenar en algún sitio archivos que puedan accederse y mostrarse fácilmente en tu app. Aquí entra en juego la posibilidad de subir archivos manualmente desde la Consola de Firebase. Bajo la pestaña Files, verás un botón azul titulado Upload File (Subir Archivo).

Button for manually uploading files to Firebase storageButton for manually uploading files to Firebase storageButton for manually uploading files to Firebase storage

Clickéalo, selecciona el archivo que quieres subir, y aparecerá en tu Almacenamiento de Firebase.

An uploaded fileAn uploaded fileAn uploaded file

Al seleccionar dicho archivo en la consola, se mostrará una vista detallada, que te permitirá inspeccionar archivos previamente subidos.

An uploaded file detailsAn uploaded file detailsAn uploaded file details

Descarga de Archivos desde una App Android

Ahora que has guardado un archivo en Firebase, prosigamos y mostrémoslo en una app. Usaremos un layout simple en nuestra MainActivity, que contiene una ImageView con un id "image".

1
<?xml version="1.0" encoding="utf-8"?>
2
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
3
    android:layout_width="match_parent"
4
    android:layout_height="match_parent">
5
6
    <ImageView
7
        android:id="@+id/image"
8
        android:layout_width="wrap_content"
9
        android:layout_height="wrap_content" />
10
</RelativeLayout>

Para poder acceder a tus archivos en el Almacenamiento de Firebase, primero necesitarás obtener una referencia al objeto FirebaseStorage, y luego crear una StorageReference a la URL de tu proyecto y el archivo que quieres descargar. Puedes encontrar la URL de tu proyecto en la parte superior de la sección Files dentro de Storage, en la Consola de Firebase.

1
FirebaseStorage storage = FirebaseStorage.getInstance();
2
StorageReference storageRef = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("android.jpg");

A continuación, puedes crear un objeto File e intentar cargar el archivo que deseas invocando getFile en tu StorageReference, con el nuevo objeto File pasado como parámetro. Como esta operación se ejecuta asincrónicamente, puedes agregarle un OnSuccessListener y un OnFailureListener a tu llamada, para manejar cada contingencia.

1
try {
2
    final File localFile = File.createTempFile("images", "jpg");
3
    storageRef.getFile(localFile).addOnSuccessListener(new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
4
        @Override
5
        public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
6
            Bitmap bitmap = BitmapFactory.decodeFile(localFile.getAbsolutePath());
7
            mImageView.setImageBitmap(bitmap);
8
9
        }
10
    }).addOnFailureListener(new OnFailureListener() {
11
        @Override
12
        public void onFailure(@NonNull Exception exception) {
13
        }
14
    });
15
} catch (IOException e ) {}

En el método onSuccess() del OnSuccessListener, puedes obtener el objeto FileDownloadTask.TaskSnapshot y recuperar el archivo, y así podremos asignarle esta imagen a nuestro ImageView.

Image downloaded from Firebase storage displayed in appImage downloaded from Firebase storage displayed in appImage downloaded from Firebase storage displayed in app

Descarga de Archivos como Array de Bytes

Si sólo necesitas descargar el archivo como un byte[], y no lo necesitas como un archivo, que es el caso más probable al cargar una imagen dentro de un ImageView, puedes recuperar los bytes de una forma similar.

1
final long ONE_MEGABYTE = 1024 * 1024;
2
storageRef.getBytes(ONE_MEGABYTE).addOnSuccessListener(new OnSuccessListener<byte[]>() {
3
    @Override
4
    public void onSuccess(byte[] bytes) {
5
        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
6
        mImageView.setImageBitmap(bitmap);
7
    }
8
});

Obtener la URL de un Archivo

Podría haber situaciones donde no necesites el contenido real de un archivo almacenado, sino que prefieras la URL. Puedes hacer esto de manera parecida a los dos ejemplos anteriores usando el método getDownloadUrl() en tu StorageReference, que te devolverá una Uri apuntando a la ubicación del archivo.

1
storageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
2
    @Override
3
    public void onSuccess(Uri uri) {
4
        Log.e("Tuts+", "uri: " + uri.toString());
5
        //Handle whatever you're going to do with the URL here

6
    }
7
});

El método expuesto arriba imprimirá en el Android Monitor la URL de la imagen que antes subimos manualmente.

1
E/Tuts+: uri: https://firebasestorage.googleapis.com/v0/b/tutsplus-firebase.appspot.com/o/android.jpg?alt=media&token=1828111c-78e7-4640-b418-c65e7571576a

Subida desde una App Android

Ahora que ya sabes cómo descargar archivos desde Firebase, es hora de trabajar en la subida de archivos. Como vimos en la descarga desde el Almacenamiento de Firebase, los procesos para cada formato de tu contenido son bastante similares. La subida no es diferente, por lo que simplemente nos enfocaremos en cómo mover archivos desde tu app hacia el Almacenamiento de Firebase.

Subida de un Array de Bytes

Al igual que en la descarga, necesitarás obtener una referencia al objeto FirebaseStorage y crear una referencia a la nueva ubicación de tu archivo como una StorageReference. En este ejemplo, mostraremos ic_launcher.png en tu ImageView, y luego lo subiremos como un array de bytes.

1
FirebaseStorage storage = FirebaseStorage.getInstance();
2
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("ic_launcher.png");

Luego, necesitaremos crear un array de bytes de la imagen guardada en memoria en el ImageView. Esto se hace tratándola como un Bitmap, comprimiéndola en un ByteArrayOutputStream, y luego convirtiendo este último a un byte[].

1
mImageView.setDrawingCacheEnabled(true);
2
mImageView.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
3
mImageView.layout(0, 0, mImageView.getMeasuredWidth(), mImageView.getMeasuredHeight());
4
mImageView.buildDrawingCache();
5
Bitmap bitmap = Bitmap.createBitmap(mImageView.getDrawingCache());
6
7
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
8
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
9
byte[] data = outputStream.toByteArray();

Finalmente, puedes crear una UploadTask invocando putBytes(byte[]) para subir tu imagen a Firebase. Esta UploadTask puede tener además un OnSuccessListener y un OnFailureListener asociados.

1
UploadTask uploadTask = storageReference.putBytes(data);
2
uploadTask.addOnFailureListener(new OnFailureListener() {
3
    @Override
4
    public void onFailure(@NonNull Exception exception) {
5
    }
6
}).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
7
    @Override
8
    public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
9
    }
10
});

Cuando revises la página de almacenamiento en la Consola de Firebase, deberías ver ic_launcher.png en tu listado de archivos.

Details from a programmatically uploaded imageDetails from a programmatically uploaded imageDetails from a programmatically uploaded image

Subida Desde un InputStream o un Archivo

Ahora que sabes cómo subir un array de bytes, los otros dos tipos de subida deberían ser bastante intuitivos. Supongamos que tenemos un archivo de texto llamado test.txt en nuestra carpeta resources/raw. Podemos leerlo desde un InputStream y luego subirlo mediante el método putStream(InputStream) de StorageReference.

1
FirebaseStorage storage = FirebaseStorage.getInstance();
2
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("test.txt");
3
4
InputStream stream = getResources().openRawResource(R.raw.test);
5
6
UploadTask uploadTask = storageReference.putStream(stream);
List of uploaded images including text file from InputStreamList of uploaded images including text file from InputStreamList of uploaded images including text file from InputStream

La subida de un archivo existente es igual de sencilla: simplemente debes obtener una referencia al archivo e invocar putFile(Uri) con una URI que apunte a tu archivo. En este ejemplo, crearemos un archivo temporal vacío en nuestro código.

1
FirebaseStorage storage = FirebaseStorage.getInstance();
2
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("test2.txt");
3
4
File file = null;
5
try {
6
    file = File.createTempFile("test2", "txt");
7
} catch( IOException e ) {
8
9
}
10
UploadTask uploadTask = storageReference.putFile(Uri.fromFile(file));
Raw file upload shown in Firebase storageRaw file upload shown in Firebase storageRaw file upload shown in Firebase storage

Control de Subidas y Uso de Callbacks

Los archivos que hemos subido hasta el momento son pequeños, pero habrá veces que necesites subir archivos más grandes que tomen más cantidad de tiempo. Firebase provee algunos métodos en UploadTask que te permiten controlar el flujo de una subida y atender a su progreso y cambios de estado. Estos métodos incluyen pause(), resume(), y cancel(). pause() y resume() permiten pausar y continuar una UploadTask, mientras que cancel() la detendrá por completo. Además, puedes utilizar onPauseListener y onProgressListener para atender al progreso y los estados de pausa de la subida.

1
mPause = (Button) findViewById(R.id.pause);
2
mResume = (Button) findViewById(R.id.resume);
3
4
mPause.setOnClickListener(new View.OnClickListener() {
5
    @Override
6
    public void onClick(View view) {
7
        mUploadTask.pause();
8
    }
9
});
10
11
mResume.setOnClickListener(new View.OnClickListener() {
12
    @Override
13
    public void onClick(View view) {
14
        mUploadTask.resume();
15
    }
16
});
17
18
19
FirebaseStorage storage = FirebaseStorage.getInstance();
20
StorageReference storageReference = storage.getReferenceFromUrl("gs://tutsplus-firebase.appspot.com").child("image.jpg");
21
22
InputStream stream = getResources().openRawResource(R.raw.image);
23
24
mUploadTask = storageReference.putStream(stream);
25
26
mUploadTask.addOnPausedListener(new OnPausedListener<UploadTask.TaskSnapshot>() {
27
    @Override
28
    public void onPaused(UploadTask.TaskSnapshot taskSnapshot) {
29
        Log.e("Tuts+", "upload paused!");
30
    }
31
});
32
33
mUploadTask.addOnProgressListener(new OnProgressListener<UploadTask.TaskSnapshot>() {
34
    @Override
35
    public void onProgress(UploadTask.TaskSnapshot taskSnapshot) {
36
        Log.e("Tuts+", "Bytes uploaded: " + taskSnapshot.getBytesTransferred());
37
    }
38
});

El código de arriba te permitirá controlar el proceso de subida para una imagen medianamente grande (1MB, en este caso) y ver sus estados cambiando en el log de Android al presionar los botones Pausar y Continuar.

1
E/Tuts+: Bytes uploaded: 524288
2
E/Tuts+: upload paused!
3
E/Tuts+: Bytes uploaded: 786432

Manejo del Ciclo de Vida de una Activity en Android

Como cualquier desarrollador Android puede atestiguar, a veces el ciclo de vida de una Activity en Android puede ocasionar problemas inesperados. Una de las causas frecuentes de problemas son los listeners que duran más tiempo que su Activity madre, por ejemplo, listeners de éxito o error vinculados a una tarea de almacenamiento de Firebase.

Si una Activity es destruida y recreada (como sucede al rotar la pantalla) mientras una tarea se está ejecutando, podrías obtener un NullPointerException cuando la tarea se complete. Para evitarlo, debes guardar tu StorageReference como un String en el Bundle referenciado en el método onSaveInstanceState(Bundle), y luego recuperarlo y agregar listeners de éxitos a cada FileDownloadTask o FileUploadTask asociada a esa StorageReference.

1
@Override
2
protected void onSaveInstanceState(Bundle outState) {
3
    super.onSaveInstanceState(outState);
4
5
    if (mStorageReference != null) {
6
        outState.putString(EXTRA_STORAGE_REFERENCE_KEY, mStorageReference.toString());
7
    }
8
}
9
10
@Override
11
protected void onRestoreInstanceState(Bundle savedInstanceState) {
12
    super.onRestoreInstanceState(savedInstanceState);
13
14
    final String stringRef = savedInstanceState.getString(EXTRA_STORAGE_REFERENCE_KEY);
15
16
    if (stringRef == null) {
17
        return;
18
    }
19
20
    mStorageReference = FirebaseStorage.getInstance().getReferenceFromUrl(stringRef);
21
    List<FileDownloadTask> tasks = mStorageReference.getActiveDownloadTasks();
22
    for( FileDownloadTask task : tasks ) {
23
        task.addOnSuccessListener(this, new OnSuccessListener<FileDownloadTask.TaskSnapshot>() {
24
            @Override
25
            public void onSuccess(FileDownloadTask.TaskSnapshot taskSnapshot) {
26
                Log.e("Tuts+", "download successful!");
27
            }
28
        });
29
    }
30
}

Conclusión

En este tutorial has aprendido mucho acerca de Firebase y las opciones de que dispone para almacenamiento de archivos. Ahora deberías poder subir y descargar archivos, controlar transferencias de datos, y poder manejar los cambios en el ciclo de vida de la Activity de tu app mientras que ocurre una transacción.

Aunque apenas hemos visto superficialmente lo que se puede hacer con Firebase, el conocimiento de esta herramienta te permitirá expandir las capacidades de tus propias aplicaciones y proveer una excelente experiencia a tus usuarios.

¡Mientras tanto, revisa algunos de nuestros demás artículos sobre desarrollo en Android!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.