Android: Subir múltiples imágenes con okhttp v3

okhttp3

OkHttp es un cliente HTTP muy eficiente para nuestros proyectos Android. Esta librería nos permite manejar múltiples peticiones sin demasiadas preocupaciones y configurar el cliente HTTP para que se adapte a nuestras necesidades. Podemos encontrar esta librería en GitHub y toda la información necesaria para comenzar a trabajar con ella, os dejo el enlace.

En esta entrada vamos a ver como podemos subir a un servidor una o varias imágenes con okHttp. En primer lugar vamos a agregar la dependencia de esta librería en el gradle dentro del módulo app:

dependencies {
    ...
    compile 'com.squareup.okhttp3:okhttp:3.9.1'
    ...
}

Para los que utilizáis maven:

<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
  <version>3.9.1</version>
</dependency>

Una vez agregada la dependencia Android Studio nos pedirá que compilemos pulsando el texto “sync” de la notificación que nos muestra. Ahora debemos declarar e instanciar un cliente de la clase OkHttp, yo normalmente lo declaro al arrancar la aplicación en la clase que extiende de “Application” o de “MultiDexApplication”.

public class App extends MultiDexApplication {

    public static final OkHttpClient okHttpClient = new OkHttpClient();

}

De este modo puedo acceder al cliente desde cualquier clase de la aplicación sin tener que instanciar uno en cada clase o en cada petición.

...
App.okHttpClient.newCall(request).enqueue(new Callback ...)
...

A continuación declaramos el método encargado de subir las imágenes. Este método contiene una petición asíncrona (OkHttp nos permite realizar peticiones síncronas y asíncronas) y añade las imágenes por POST al igual que los demás parámetros.

private void uploadImages(List newPhotos) {
    try {

        if (!AppController.isInternetConnected(this)) {
            Toast.makeText(getApplicationContext(), "No tienes conexión a internet.", Toast.LENGTH_SHORT).show();
            return;
        }

        // Mostramos el diálogo de progreso.
        showProgressDialog();

        MultipartBody.Builder multipartBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);

        int length = newPhotos.size();
        int imageCount = 0;

        for (int i = 0; i < length; i++) {
            // Getting local Photo URL.
            Photo photo = newPhotos.get(i);
            String photoUrl = photo.getPath();

            File sourceFile = new File(photoUrl);

            if(sourceFile.exists()) {
                // Changing Media Type whether JPEG or PNG
                final MediaType MEDIA_TYPE = MediaType.parse(photoUrl.endsWith("png") ? "image/png" : "image/jpeg");

                // Adding in MultipartBuilder
                multipartBuilder.addFormDataPart("image" + (i + 1), sourceFile.getName(), RequestBody.create(MEDIA_TYPE, sourceFile));

                // Counting No Of Images
                imageCount++;
            }
        }


        RequestBody requestBody = multipartBuilder
                .addFormDataPart("action", "UPLOAD_IMAGES")
                .addFormDataPart("email", user.getEmail())
                .addFormDataPart("token", user.getToken())
                .addFormDataPart("images_count", String.valueOf(imageCount))
                .build();

        okhttp3.Request request = new okhttp3.Request.Builder()
                .url(AppController.urlBase)
                .post(requestBody)
                .build();


        App.okHttpClient.newCall(request).enqueue(new Callback() {

            @Override
            public void onResponse(@NonNull Call call, @NonNull okhttp3.Response response) throws IOException {
                String responseStr = response.body().string();
                Log.i(TAG, "responseStr : "+ responseStr);

                try {
                    responseStr = responseStr.substring(2, responseStr.length());

                    JSONObject jsonObject = new JSONObject(responseStr);

                    if (jsonObject.has("success")) {
                        int success = jsonObject.optInt("success", 0);

                        if (success == 1) {
                            FotosActivity.this.runOnUiThread(new Runnable() {
                                public void run() {
                                    Toast.makeText(FotosActivity.this, "Imágenes subidas con éxito.", Toast.LENGTH_LONG).show();
                                    finish();
                                }
                            });
                        } else {
                            FotosActivity.this.runOnUiThread(new Runnable() {
                                public void run() {
                                    Toast.makeText(FotosActivity.this, "Se ha producido un error al subir las imágenes. Vuelve a intentarlo.", Toast.LENGTH_SHORT).show();
                                }
                            });
                        }

                    } else {
                        FotosActivity.this.runOnUiThread(new Runnable() {
                            public void run() {
                                Toast.makeText(FotosActivity.this, "Se ha producido un error al subir las imágenes. Vuelve a intentarlo.", Toast.LENGTH_SHORT).show();
                            }
                        });
                    }

                    // Cerramos el diálogo de progreso.
                    closeProgressDialog();

                } catch (JSONException e) {
                    e.printStackTrace();
                    // Cerramos el diálogo de progreso.
                    closeProgressDialog();
                }
            }

            @Override
            public void onFailure(@NonNull Call call, @NonNull IOException e) {
                Log.e(TAG, "Error al intentar subir imágenes: " + e.getLocalizedMessage());
                FotosActivity.this.runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(FotosActivity.this, "Se ha producido un error al subir las imágenes. Vuelve a intentarlo.", Toast.LENGTH_SHORT).show();
                    }
                });
                // Cerramos el diálogo de progreso.
                closeProgressDialog();
            }
        });


    } catch (Exception e) {
        Log.e(TAG, "Error al intentar subir imágenes: " + e.getLocalizedMessage());
        FotosActivity.this.runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(FotosActivity.this, "Se ha producido un error al subir las imágenes. Vuelve a intentarlo.", Toast.LENGTH_SHORT).show();
            }
        });
        // Cerramos el diálogo de progreso.
        closeProgressDialog();
    }

}

Hay varias partes en este método. La primera es la comprobación de la conexión del dispositivo. Si no hay conexión no hace falta realizar la petición ya que producirá error, así que mostramos un mensaje en pantalla informando al usuario y salimos del método con la instrucción “return”. Dejo el método que comprueba la conexión.

public static boolean isInternetConnected(Context context) {
    ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
    NetworkInfo netInfo = cm.getActiveNetworkInfo();
    return netInfo != null && netInfo.isConnectedOrConnecting();
}

La segunda parte es cuando mostramos un ProgressDialog al usuario para que sepa que estamos realizando el proceso de subida de imágenes.

private void showProgressDialog() {
    if (progressDialog == null)
        progressDialog = new ProgressDialog(this);

    progressDialog.setIndeterminate(true);
    progressDialog.setTitle(getResources().getString(R.string.progress_dialog_title));
    progressDialog.setMessage(getResources().getString(R.string.progress_dialog_message));

    if (!progressDialog.isShowing())
        progressDialog.show();
}


private void closeProgressDialog() {
    if (progressDialog.isShowing())
        progressDialog.dismiss();
}

En la tercera parte creamos la petición y mediante un “for” recorremos la lista de objetos del tipo “Photo”. Cada objeto “photo” tiene varios atributos, uno de ello es el “path” (la ruta) en el que se encuentra guardada la imagen (vosotros podéis definir cualquier objeto o pasarle las rutas directamente en un array de tipo String). A partir de esta ruta creamos un objeto “File” pasándole la ruta de la imagen y la añadimos a la petición con el método “addFormData”. A este método le pasamos el nombre del parámetro  POST que queremos recibir en el servidor, el nombre de la imagen y la imagen.  Añadimos el resto de parámetros a la petición de nuevo con el método “addFormData” y llamamos al método “build” para crear el cuerpo de la petición y recogerlo en una variable del tipo “RequestBody”. Creamos e instanciamos un objeto del tipo “Request” pasándole la url a la que realizamos la petición, el cuerpo de la petición “RequestBody” que contiene todos los parámetros y llamamos al método build para que nos cree la petición.

Finalmente hay que acceder al cliente OkHttp y pasarle la petición con el método “newCall” y llamar al método enqueu para que la librería ponga en la cola de llamadas nuestra petición y la procese. En el callback podremos analizar si la petición se ha completado correctamente o ha fallado y realizar unas u otras acciones según nos convenga.

Pd: Los toast que se muestra en los métodos del “callback” se crean dentro del método “runOnUiThread” debido a que dentro del callback aún estamos en un hilo secundario y no podemos acceder al hilo primario o a sus vistas .

 

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión /  Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión /  Cambiar )

w

Conectando a %s