Aitor Sánchez - Blog - Nov. 1, 2023, 11:28 a.m.
¿Necsitas enviar una archivo a un servidor y nos sabes cómo? O, quizás, lo que necesitas es descargarte información desde una api rest ¿verdad? Si has respondido que si, esto quizás sea para ti. Sigue leyendo.
Mi nombre es Aitor, soy desarrollador de apps desde 2014, y en este artículo vas a aprender, de una manera efectiva, a enviar y recibir datos por internet cómo si de un repartidor de amazón se tratase.
Pero antes de continuar, esta es la Flutter Mafia. Es mi newsletter donde tu vas a aprender a hacer apps y a ganar dinero con ellas junto con otros genietes que ya están dentro. Y si te suscribes te regalo mi ebook "Duplica los ingreso de tus apps en 5 minutos" No es broma, quizás te interese.
Bien, esta es una respuesta que seguramente ya conozcas, pero si no es el caso y quieres saber para qué se utiliza te cuento un poco.
De manera general, este objeto sirve para poder comunicarnos con un servidor mediante peticiones a través del protocolo Http.
Nota: Si queremos hacer peticiones Https contamos con un objeto muy similar a alto nivel llamado HttpsUrlConnection.
Básicamente es eso, una manera de poder comunicar nuestra aplicación con el exterior.
Una vez ya sabemos todo esto vamos a continuar hablando de cosillas sobre este objeto a ver hasta dónde podemos llegar.
Este objeto cuenta con numerosos métodos/funciones que nos permiten la comunicación de una manera muy sencilla. Vamos a comenzar por mostrar un ejemplo práctico sobre el tema:
Como puedes ver, utilizamos, independientemente de la clase HttpUrlConnection, varias clases más como puede ser la clase URL, la clase BufferedReadero o el InputStream. Aunque este ejemplo es bastante pobre ilustra perfectamente el fin que queremos lograr.
En primer lugar, decir que esta metodología ha sido sustituida por otro algoritmo. Si venías buscando alguna explicación sobre este método de extraer datos estás en el sitio correcto, vas a aprender a hacer mejor las cosas y a actualizar tus conocimientos.
Os presento el objeto BufferedReader. Es una clase que nos permitirá leer el buffer de entrada de la conexión de una manera mucho más eficiente que la clase Anterior.
Como hemos dicho antes, este objeto se encarga de leer el buffer de entrada para así ser legible para nosotros.
No necesariamente tiene porque ser sobre una petición Http, pero es el fin que buscamos en este artículo.
Para construirlo necesitamos un objeto adicional que se llama InputStreamReader, del que daremos más información en otro artículo, que a su vez recibe como parámetro un InputStream.
¿A que no adivináis de donde vamos a sacar el InputStream? Correcto, del objeto HttpUrlConnection con su función “getInputStream”.
Vamos a ver una construcción más visual para nosotros del objeto.
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
Esta parte es muy importante, aunque directamente no se la decimos a la clase con un campo o una función, es una buena práctica, independientemente de que lo necesite o no, codificar nuestras cadenas con una codificación UTF 8 para evitar problemas de compatibilidad con Ñs o acentos rarunos.
También cabe mencionar aquí que, en las últimas versiones de Java, y como estamos hablando de Android nos afecta, no es necesario este tipo de procesos. En java las cadenas llevan por defecto una codificación UTF 16. Pero me ha parecido correcto que lo sepas porque en otros lenguajes de programación no sucede esto.
Esto sí que es algo que los programadores seguimos utilizando, no todos, pero mucho sí. Esta función es la encargada de decirnos si la petición es correcta.
¿A qué te refieres Aitor? Muy sencillo. Todas las peticiones HTTP contra un servidor tiene un código de estado, y lo podemos controlar desde HttpUrlConnection. Este código es el que nos notifica a nosotros si todo ha ido correctamente, ha fallado, hay un error en el servidor o cualquier otra cosa que haya sucedido.
Esta función nos va a devolver un entero para informarnos de lo que hemos comentado arriba. Aquí te voy a poner una lista de los más comunes.
Podría estás aquí un mes listando errores. Pero como sé que eres un ti@ espabilado, ya sabes por donde voy y no es necesario que sigamos por aquí, ¿verdad?
Vale, me ha parecido interesante hablar de este objeto aquí, aunque luego profundizaremos más en él en otro artículo, porque es el fiel compañero de nuestra clase.
Nos permitirá de una manera muy sencilla y rápida tratar los datos que nos llegan en la respuesta desde un api RestFull.
Para quien no lo sepa, un api RestFull es un sistema de comunicación basado en texto plano para el envío y la recepción de datos. Las características que tiene este texto es que tiene que estar codificado como archivo JSON, una estructura estándar para todos los lenguajes que nos permitirá la comunicación entre diferentes tecnologías y lenguajes de programación.
Hasta que no tengamos publicado el artículo, puedes informarte más a fondo en este enlace.
Genial, ¡¡¡ya vamos avanzando!!! Ahora vamos a hablar un poco de cómo podemos enviar archivos al servidor desde una aplicación Android.
Los pasos a seguir son muy similares a lo que hemos visto en el ejemplo de arriba. Simplemente tendremos que extraer los bytes de un archivo file y mandarlos mediante la clase OutputStream o derivados.
Pero antes de continuar dando la monserga, vamos a ver un ejemplo.
try
{
//------------------ PETICIÓN DESDE CLIENTE
// PHP Conexión con el servicio
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setDoInput(true);
conn.setDoOutput(true);
conn.setUseCaches(false);
conn.setRequestMethod("POST");
conn.setRequestProperty("Connection", "Keep-Alive");
conn.setRequestProperty("Content-Type", "multipart/form-data;boundary="+boundary);
DataOutputStream dos = new DataOutputStream( conn.getOutputStream() );
dos.writeBytes(twoHyphens + boundary + lineEnd);
dos.writeBytes("Content-Disposition: form-data; name=\"archivo[]\";filename=\"" + existingFileName +"\"" + lineEnd);
dos.writeBytes(lineEnd);
Log.e(Tag,"Las cabeceras ya están escritas");
int bytesAvailable = fileInputStream.available();
int maxBufferSize = 1024;
int bufferSize = Math.min(bytesAvailable, maxBufferSize);
byte[] buffer = new byte[bufferSize];
int bytesRead = fileInputStream.read(buffer, 0, bufferSize);
while (bytesRead > 0)
{
dos.write(buffer, 0, bufferSize);
bytesAvailable = fileInputStream.available();
bufferSize = Math.min(bytesAvailable, maxBufferSize);
bytesRead = fileInputStream.read(buffer, 0, bufferSize);
}
dos.writeBytes(lineEnd);
dos.writeBytes(twoHyphens + boundary + twoHyphens + lineEnd);
// Cerramos el inputStream
Log.e(Tag,"El archivo ya se ha subido");
fileInputStream.close();
dos.flush();
InputStream is = conn.getInputStream();
// Recuperamos la respuesta desde el servidor
int ch;
StringBuffer b =new StringBuffer();
while( ( ch = is.read() ) != -1 ){
b.append( (char)ch );
}
String s=b.toString();
Log.i("Response",s);
dos.close();
}
catch (MalformedURLException ex)
{
Log.e(Tag, "error: " + ex.getMessage(), ex);
}
En primer lugar, tenemos que decirle a nuestro objeto que vamos a utilizar un tipo de contenido específico para poder enviar la foto al servidor. Para este fin vamos a usar la función “setRequestProperthy” y le vamos a pasar como valor un String que contenta lo siguiente como primer parámetro:
“Content-Type"
Como segundo parámetro:
"multipart/form-data”
Después, usando función “addRequestProperty” le vamos a decir que la conexión se mantenga abierta hasta que la imagen sea subida por completo. Utilizaremos la misma función, pero los valores que recibe son diferentes:
"Connection", "Keep-Alive" //En este orden
Como podéis ver en la imagen, descomponemos el archivo File, que es donde está la imagen, en una array de bytes. Seteamos la longitud máxima de bytes que vamos a escribir con la función “setChunkedLenghtStreamingMode” de nuestro objeto HttpUrlConnection. Con esta función le decimos al sistema cual es la longitud que tiene que aceptar desde el buffer de escritura.
Una vez ya tenemos construido todo el sistema solo nos queda escribir la imagen en el Buffer de salida y que se envíe al servidor. Para eso utilizaremos nuestro Buffer de escritura con su función “writeBytes” pasándole como parámetro el array de bytes que hemos descomprimido de nuestra imagen.
Se que es un poco lioso en un principio, pero cuando ya le coges el gusto se hace muy fácil. Es más, te invito a que te hagas un repositorio propio para que metas todo este código generado por ti mismo y que en un futuro te podrá ayudar a golpe de click. Más adelante hablaremos como podemos hacer esto y os explicaré como lo utilizo yo.
En el punto de antes hemos visto cómo hemos agregado dos cabeceras a la petición. La cabecera “Content-type” y la cabecera “Connection” pero simplemente lo hemos hecho, pero no lo hemos explicado.
Llegados a este punto, imagino que sabes lo que es una cabecera, pero si no lo sabes aquí te dejo un enlace donde puedes profundizar un poco en el tema:
Para poder agregar cabeceras a nuestra petición basta con utilizar los dos métodos que hemos visto antes, “addRequestProperty” y “setRequestProperty”. Ambos métodos reciben dos parámetros en formato String. El primero de ellos es el nombre de la cabecera que queremos enviar y el segundo es el valor de la cabecera.
Antes hemos hablado de dos, Content-type y Connection, pero puedes incluir cualquier dentro del estándar. Pero también podremos enviar nuestras propias cabeceras.
Por ejemplo, aunque no es la mejor manera, podemos enviar los datos de un usuario mediante este tipo de función y después capturarla desde el servidor:
“addRequestProperty(“Id-usuario”, idUsuario);”
¿Ves que sencillo es? Aunque no sea el mejor ejemplo, es uno útil y para entenderlo sobra.
Llegados a este punto, quizás ya estés un poco cansado de artículo, es lo más normal. Es algo denso, pero quiero que sientes unas buenas bases sobre este tema antes de continuar.
Ves al baño, si es necesario, prepara un café y descansa un poco que continuamos en 5 minutos. No te preocupes que yo no me voy de aquí J
¿Ya has vuelto? Genial, continuemos. Ahora vamos a hablar de cómo podemos usar los métodos para hacer peticiones. Para quien no lo sepa, aunque si has llegado hasta aquí dudo que no lo sepas aún, por método entendemos la manera en la que vamos a hacer la petición al servidor.
Los más utilizados son:
“POST”, que se utiliza para enviar los datos en la petición.
“GET”, se utiliza para enviar datos en la url. Se puede identificar si la URL tiene el símbolo “?”.
Pero también podemos encontrar en otros casos “PUT”, “DELETE” y “OPTIONS” de los que ya hablaremos más adelante.
Para decirle a nuestro objeto tenemos que usar su método “setRequestMethod” que recibe como parámetro un String con el método a utilizar, veamos un ejemplo:
conn.setRequestMethod("POST"); //Así le decimos que la petición va através de POST
conn.setRequestMethod("GET"); //Así mediante GET, es la opción por defecto
Perfecto, ya va llegando el artículo a su fín. Te explico, este objeto no es exclusivo del api de Android. De hecho, está en el api de java y por eso podemos usarlo en Android. El uso es el mismo, pero hay una diferencia en el sistema que te voy a explicar cuál es para terminar el artículo.
¿Sabes lo que son los Thread (hilos)? Pues bien, esta explicación va en ese camino.
En java, aunque no recomendable, se nos permite realizar peticiones Http en el hilo principal. Pero esto converge en un bloqueo del hilo principal hasta que la conexión no escupa una respuesta.
En Android no se nos permite hacer peticiones Http en el Main Thread. En el momento que lo hagamos la aplicación se cerrará y devolverá un ANR por este motivo. Para solucionarlo tenemos que ejecutar estas funciones en hilo independiente con la clase “Thread” o “AsyncTask” de las que hablaremos más adelante.
Este vídeo no tiene desperdicio :)
Mira, en el momento que tu mejoras el logo de una app que tengas publicada en Google Play, las descargas y los ingresos que esta aplicación genera aumentan. Esto es así. Mejor logo es igual a más dinero.
Basándonos en esto, hemos creado esta herramienta que te permite evaluar, optimizar y mejorar los logos de tus apps para que reciban más descargas. No te quiero espoilear, dentro hay un video explicativo. Entra en el enlace.
Pues bien, geniete. Espero haberte ayudado a dar respuesta a tus preguntas y nos vemos en el siguiente artículo. Hasta entonces ¡que vaya bien!