Usando Firebase Storage con React.js
¿Ves alguna errata o quieres modificar algo? Haz una Pull Request
En este artículo vamos a ver como utilizar la funcionalidad de Storage de Firebase para poder subir y almacenar archivos en la nube, y como integrarlo con React.
Firebase Storage es algo similar a lo que puedes conseguir con Amazon S3. Un espacio en la nube donde almacenar archivos binarios (imágenes, ficheros de audio, video, etc...)
A través de su API es muy sencillo subir archivos desde una SPA, y en particular con React. Vamos a ello.
Configuración de Firebase.
Lo primero que necesitamos en entrar en la consola de firebase y crear un proyecto web nuevo, después copiar la configuración y añadirla a nuestro código. Esto es igual para todas las apps que hagamos con Firebase, por tanto para ésta parte puedes guiarte del tutorial que escribí anteriormente y que puedes encontrar aquí
Componente FileUpload
Vamos a crear un componente llamado FileUpload
, cuyo formato será el siguiente:
class FileUpload extends React.Component {
render() {
return (
<div>
<progress value={this.state.uploadValue} max="100">
{this.state.uploadValue} %
</progress>
<br />
<input type="file" onChange={this.handleOnChange.bind(this)} />
<br />
<img width="90" src={this.state.picture} />
</div>
);
}
}
Vamos a tener un elemento de tipo progress
para mostrar el porcentaje de progreso de la subida del archivo y un input
de tipo file
para cargar los archivos. Por último tendremos un img
donde mostraremos el archivo una vez subido con su URL apuntando a firebase.
El valor de progress
va a estar referenciado por el estado del componente, de esta manera, cada vez que cambie, el componente se re-renderizará con el nuevo valor.
De igual manera, el atributo src
del elemento img
también es una referencia al estado del componente, para que sólo se muestre cuando esté subido.
Por tanto, nuestro Componente FileUpload
debe tener un constructor donde inicialicemos el estado:
class FileUpload extends React.Component {
constructor () {
super()
this.state = {
uploadValue: 0
}
}
render() {...}
}
Gestión del evento
En el input
de tipo file
tenemos un evento onChange
que se disparará cada vez que carguemos un fichero nuevo. Este evento va a llamar a la función handleOnChange
que vamos a definir a continuación
En ésta función lo primero que vamos a hacer es tomar las referencias del fichero que estamos añadiendo en el input, una referencia al storage de nuestro proyecto en firebase donde tendremos un bucket o carpeta que llamaremos pictures
y el nombre del fichero que vamos a subir y la referencia a la tarea de subida:
handleOnChange (event) {
const file = event.target.files[0]
const storageRef = firebase.storage().ref(`pictures/${file.name}`)
const task = storageRef.put(file)
}
La tarea que acabamos de crear, tiene una serie de listener que se dispararán en cuanto ocurra un evento. El que nos interesa es state_changed
que nos avisa en todo momento de la subida del fichero y nos da un snapshot con los bytes transferidos hasta el momento. Esto nos permite actualizar el estado y la barra de progreso.
Además de eso, nos proprociona un callback para gestionar los errores si los hubiese y una última función para indicarnos que la subida se ha realizado
task.on(
"state_changed",
(snapshot) => {
// Se lanza durante el progreso de subida
},
(error) => {
// Si ha ocurrido un error aquí lo tratamos
},
() => {
// Una vez se haya subido el archivo,
// se invoca ésta función
}
);
Para la primera función, simplemente vamos a hacer una operación que nos devuelva el porcentaje subido sobre el total del tamaño del archivo y actualice el estado y así la barra de progreso:
task.on("state_changed", (snapshot) => {
let percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
this.setState({
uploadValue: percentage,
});
});
Y cuando el fichero se haya subido totalmente, vamos a actualizar nuevamente el estado con la URL de la imagen en firebase storage, que tomamos de la referencia a la tarea:
task.on('state_changed', (snapshot) => {
...
}, (error) => {
...
}, () => {
this.setState({
picture: task.snapshot.downloadURL
})
})
Y listo! ya tenemos nuestro cargador de ficheros :)
El código completo de éste ejemplo lo tienes a continuación:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import * as firebase from 'firebase'
firebase.initializeApp({
apiKey: 'AIzaS*******************',
authDomain: '*****',
databaseURL: '******',
storageBucket: '******',
messagingSenderId: '**********'
})
class FileUpload extends Component {
constructor () {
super()
this.state = {
uploadValue: 0
}
}
handleOnChange (e) {
const file = event.target.files[0]
const storageRef = firebase.storage().ref(`pictures/${file.name}`)
const task = storageRef.put(file)
task.on('state_changed', (snapshot) => {
let percentage = (snapshot.bytesTransferred / snapshot.totalBytes) * 100
this.setState({
uploadValue: percentage
})
}, (error) => {
console.error(error.message)
}, () => {
// Upload complete
this.setState({
picture: task.snapshot.downloadURL
})
})
}
render () {
return (
<div>
<progress value={this.state.uploadValue} max='100'>
{this.state.uploadValue} %
</progress>
<br />
<input type='file' onChange={this.handleOnChange.bind(this)}/>
<br />
<img width='90' src={this.state.picture} />
</div>
)
}
}
ReactDOM.render(<FileUpload />, document.getElementById('root))
¿Quieres aprender más sobre React? Te lo enseño en mi curso online sobre fundamentos de React.js