Firebase para Android: Almacenamiento de Archivos
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.



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



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).



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).



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



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



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
.



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.



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); |



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)); |



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!