Laravel y Android: Cómo subir imágenes
Tiempo de lectura: 2.83 minutos
El día de hoy vamos a ver cómo subir una imagen desde Android a nuestro servidor web a través de nuestra API.
Obtener una imagen como Bitmap
Vamos a asumir que ya tenemos la imagen de nuestro interés en un objeto Bitmap.
La imagen la puedes obtener de múltiples formas. Por ejemplo:
- Permitir al usuario seleccionar una imagen desde galería.
- Permitir al usuario tomar una foto.
- Permitir al usuario dibujar, y capturar el resultado.
Si no sabes cómo lograr esto, asegúrate de suscribirte al canal de YouTube, que pronto agregaré tutoriales para cada uno de estos casos.
Preparar el backend (API)
En nuestro proyecto Laravel debemos crear una ruta para subir la imagen. Sobre esta ruta haremos una petición POST, enviando la imagen en formato Base64.
Para este ejemplo vamos a subir una imagen de perfil.
Es por eso que:
- Empezamos obteniendo la información del usuario en
$user
. - Obtenemos un nombre de imagen aleatorio.
- Definimos dónde queremos que se guarde nuestra imagen.
- Obtenemos la imagen desde el campo
image
presente en nuestro request. - Decodificamos el formato Base 64 para obtener el contenido de nuestra imagen.
- Creamos un archivo con el
$imageContent
, en la ruta$fullPath
que definimos previamente. - Guardamos el nombre de la imagen en la columna
photo
(tablausers
), para el usuario que está actualizando su imagen de perfil. - Finalmente, devolvemos la información del usuario en formato JSON.
public function updateImage(Request $request)
{
$user = Auth::guard('api')->user();
$imageName = $this->randomImageName();
$fullPath = public_path('/uploaded/' . $imageName);
$image = $request->image; // base64 encoded
$imageContent = $this->imageBase64Content($image);
File::put($fullPath, $imageContent);
$user->photo = $imageName;
$user->save();
return $user;
}
Ten en cuenta que esto es un ejemplo. En tu caso:
- Debes definir qué nombre de imagen quieres usar, y dónde quieres que se guarde.
- Puedes usar un nombre distinto a
image
para el campo que viene en la petición POST. - Así como guardar el nombre de la imagen en otra columna u otra tabla.
- También puedes devolver un tipo de respuesta diferente.
Del código anterior, aquí tienes los 2 métodos que hacen falta:
private function imageBase64Content($image) {
$image = str_replace('data:image/png;base64,', '', $image);
$image = str_replace(' ', '+', $image);
return base64_decode($image);
}
private function randomImageName() {
return Str::random(10) . '.' . 'png';
}
Ten en cuenta que guardamos la imagen en nuestro servidor, y luego en la base de datos solo guardamos el nombre del archivo.
Si bien no es necesario definir un mecanismo de autenticación para tu API, es lo más recomendable. Aquí puedes ver cómo agregar un sistema de autenticación para tu API en Laravel usando Passport.
Subir la imagen desde Android
Una vez que tenemos lista nuestra API, sólo nos queda hacer una petición POST desde Android, para subir nuestra imagen.
private fun postUserImage(bitmap: Bitmap) {
val call = apiService.postUserImage(
authHeader, bitmap.toBase64()
)
call.enqueue(object: Callback<User> {
override fun onFailure(call: Call<User>, t: Throwable) {
context?.toast(t.localizedMessage)
}
override fun onResponse(call: Call<User>, response: Response<User>) {
if (response.isSuccessful) {
context?.toast(getString(R.string.successful_avatar_updated))
val user = response.body()
user?.let {
Picasso.get()
.load(user.photoUrl)
.transform(CircleTransform())
.into(ivProfileImage)
}
} else {
context?.toast(getString(R.string.error_server_response))
}
}
})
}
En este ejemplo, para hacer la petición POST usamos Retrofit.
Puedes leer este artículo sobre cómo consumir una API desde Android con Retrofit. Allí vemos cómo definir un API Service.
@POST("user/image")
@Headers("Accept: application/json")
@FormUrlEncoded
fun postUserImage(
@Header("Authorization") authHeader: String,
@Field("image") base64Image: String
): Call<User>
Básicamente estamos haciendo una petición a través del método postUserImage
de nuestro API Service y enviando 2 argumentos:
- Un Bearer token (usado para la autenticación con la API), y
- La imagen codificada en formato Base 64.
Te preguntarás cómo es posible ejecutar: bitmap.toBase64()
.
Lo que ocurre es que en Kotlin tenemos extension functions. Es decir, podemos extender clases y agregar métodos adicionales.
En este caso, a la clase Bitmap, le agregué un método toBase64
:
fun Bitmap.toBase64(): String {
val stream = ByteArrayOutputStream()
compress(Bitmap.CompressFormat.JPEG, 70, stream)
return Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP)
}
Una vez hecha la petición, en cuanto el servidor responda, vamos a ejecutar onResponse
o bien onFailure
, dependiendo de si el request se pudo llevar a cabo.
Si la respuesta es exitosa, mostramos un Toast (un mensaje flotante) al usuario y así mismo le mostramos en pantalla la imagen que ha subido.
Esto último, haciendo uso de Picasso. Adicionalmente, en mi caso también definí un CircleTransform()
para mostrar la imagen en formato circular.
Conclusión
Como ves, subir una imagen y mostrarla finalmente al usuario no es nada de otro mundo.
Si quieres aprender más sobre Android y Kotlin, y cómo organizar tus proyectos, te invito a inscribirte al curso de Android y Laravel que tengo publicado ?