Leer imagen JSON base64 y almacenarla en la memoria interna

En esta entrada vamos a ver cómo al procesar una imagen JSON que viene por HTTP/POST codificada en base64. Esto lo vamos a utilizar en la práctica E0607_CitasCelebres donde obtenemos las imágenes de los autores desde el servicio web.

En versiones anteriores de la práctica CitasCelebres las imágenes que se cargan en el ImageView al pulsar el botón de un autor, estaban almacenadas como ficheros de recursos dentro de la carpeta res/drawables.

Ahora, como desde el servidor nos envían los autores y sus citas, y es posible que no tengamos el drawable del autor como recurso, tenemos que mostrarla por otra vía.

Como vemos en el encabezado del servicio get_citas_celebres.php  a continuación, hay un campo llamado DrawableB64 que contiene una imagen codificada en base64 junto con su cabecera.

/*
Recibe:
Un Json con el nombre del usuario 
JSON:
{
  "user_name": "amm01"
}

Devuelve:
Un Json con código y texto de resultado
Un array de autores, donde para cada autor se reciben sus datos y un array de sus citas 
JSON:
{
  "code": 0,
  "result": "OK",
  "autores": [ {"Nombre": "Miguel de Cervantes",
				"BiografiaUrl": "http://cervantes.es",
				"Drawable": "cervantes",
				"DrawableB64" : "Base64 encoded image",
				"Citas": [ "En un lugar de la mancha\nde cuyo nombre no quiero acordarme",
					       "No ha mucho que vivía un hidalgo ",
						   "De los de lanza en astillero", 
						   ...
						  ]
				},
				...
			  ]	
}
*/

Lo que haremos es obtener este campo como un String al cual le quitamos la  cabecera quedándonos sólo con la parte del String que corresponde al código base64 de la imagen. En la cabecera  es donde viene el tipo de imagen de que se trata, lo que podriamos utilizar para determinar que hacer con la imagen, aunque en este caso el servidor siempre nos devuelve ficheros .png con lo que obviamos la cabecera.

Lo primero es ver cómo sería el string base64 que nos envía el servidor:

"DrawableB64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAI0AAABkCAIAAAA0dyS ..... ",

Esa sería el campo, donde como vemos hasta la primera coma es la cabecera y a partir de ahí hasta que terminan las dobles comillas es la propia imagen codificada en formato base64. Lo que haremos es procesar el string de forma que nos quedaremos con todo a partir de la coma en una variable de tipo string.

strDrawableB64.substring(strDrawableB64.indexOf(",")+1

Eso lo tenemos que decodificar. Para ello tenemos la clase Base64 de la cual utilizaremos su método decode.

drawableB64 = Base64.decode(strDrawableB64.substring(strDrawableB64.indexOf(",")+1), Base64.DEFAULT);

Esto es un array de bytes, byte[] drawableB64;  con lo que podemos escribirlo a disco directamente con un outputStream.

Para ello creamos un método, en este ejemplo de la clase CitasCelebres, para que lo guarde en la memoria interna, para llamarlo haríamos:

//Guardamos el Drawable en la carpeta de ficheos privados de la app
SaveDrawabeFile(strDrawable, drawableB64);

Este método sería como sigue:

private void SaveDrawabeFile(String drawableName, byte[] b64Drawable) {
	String drawableFile=drawableName+".png";
	try{
	FileOutputStream fos = this.context.openFileOutput(drawableFile, Context.MODE_PRIVATE);
		fos.write(b64Drawable);
		fos.close();
	}
	catch (IOException e) {
		Log.e("Exception", "File write failed: " + e.toString());
	}
}

Recibe el nombre del fichero, sin la extensión .png, que se le concatena para formar la variable drawableFile.  Utilizando la el médoto openFileOutput (que es de la clase Context) obtenemos un FileOutputStream para el que simplemente llamamos a su método write pasándoles los bytes que contienen la imagen y por último a close para cerrar el stream.

El método openFileOutput devuelve un FileOutputStream para escribir en la carpeta  …./files/ del directorio privado de ficheros de la aplicación, es decir en la memoria interna del dispositivo. Como cada dispositivo puede montar dicha carpeta en una ruta diferente, si posteriormente queremos leer este fichero deberemos conocer la ruta donde se creó.

Para ello tenemos la función, también de la clase Context, getFilesDir() que nos devuelve un objeto FILE con la ruta completa.

Ahora por tanto si queremos leer el fichero guardado para mostrarlo en un ImageView por ejemplo, podríamos usar este código:

String strDrawableFile=getFilesDir()+"/"+strDrawable+".png";
Bitmap bmImg = BitmapFactory.decodeFile(strDrawableFile);
img_foto.setImageBitmap(bmImg);

Siendo img_foto un ImageView que tenemos en nuestro layout.