Blog!

Busqueda de información

Subir varios archivos en Android a un servidor

Ratio: 2 / 5

Inicio activadoInicio activadoInicio desactivadoInicio desactivadoInicio desactivado
 

Para subir varios archivos en Android a un servidor, es un poco más complicado que hacerlo en un formulario html pero si se siguen los siguientes pasos se puede hacer sin ninguna dificultad.

A grandes razgos se deben usar unas librerías de Apache para poder usar un cliente http que integre una entidad multipart en el mensaje, la cual puede estar compuesta de varios archivos, para este caso imagenes, y con ello poder subirlas al servidor.

 

1. El proyecto

Creamos un nuevo proyecto en Eclipse de Android.

  1. Click en File > New > Android Application Project.
  2. Se le da un nombre, para el caso será: SubirArchivos, y se selecciona la versión de android, de preferencia 4.0 para tener compatibilidad con la mayor catidad de dispositivos
  3. Se le dejan los valores por defencto y se da click en Siguiente, siguiente, ... hasta terminar Finish.

2. Las librerías

Ahora tenemos que descargar las siguientes librerías para poder hacer la conexión http:

  • HttpComponents: – httpclient.jar – httpcore.jar – httpmime.jar
  • Mime4j: – apache-mime4j-core-XXXX.jar

Una vez descargadas se agregan a la carpeta libs del proyecto y después se hace referencia a ellas de la siguiente manera:

  1. Se da click derecho sobre el proyecto > Properties.
  2. Del lado izquierdo de la ventana emergente se selecciona Java Build Path y la pestalla Libraries.
  3. Click en Add JARs... se selecciona el jar y se da click en OK.
  4. Lo anterior hay que repetirlo para las cuatro librerías.

3. Los xml

En el AndroidManifest.xml se debe agregar el permiso para el uso de internet y el permiso para leer archivos externos.

				
					<uses-permission android:name="android.permission.INTERNET"/>
					<ltuses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
				
			

En el layout principal main.xml se agregan tres botones para seleccionar las imagenes, tres TextView que mostrarán la ruta de la imagen seleccionada y un botón para enviar las imagenes al servidor como se muestra a continuación:

				
					<TextView
						android:id="@+id/tv_instrucciones"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:text="Seleccione las tres imagenes que desea enviar" />

					<TextView
						android:id="@+id/tv_imagen1"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignLeft="@+id/tv_instrucciones"
						android:layout_marginTop="40dp"
						android:text="Imagen 1"
						android:textAppearance="?android:attr/textAppearanceMedium" />
					
					 <TextView
						android:id="@+id/tv_imagen2"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignLeft="@+id/tv_instrucciones"
						android:layout_marginTop="140dp"
						android:text="Imagen 2"
						android:textAppearance="?android:attr/textAppearanceMedium" />

					<TextView
						android:id="@+id/tv_imagen3"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignLeft="@+id/tv_instrucciones"
						android:layout_marginTop="240dp"
						android:text="Imagen 3"
						android:textAppearance="?android:attr/textAppearanceMedium" />

				   

					<Button
						android:id="@+id/bt_seleccionar1"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignRight="@+id/tv_instrucciones"
						android:layout_marginTop="80dp"
						android:text="Seleccionar"
						android:onClick="seleccionarImagen1" />

					<Button
						android:id="@+id/bt_seleccionar2"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignRight="@+id/tv_instrucciones"
						android:layout_marginTop="180dp"
						android:text="Seleccionar" 
						android:onClick="seleccionarImagen2"/>

					<Button
						android:id="@+id/bt_seleccionar3"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignRight="@+id/tv_instrucciones"
						android:layout_marginTop="280dp"
						android:text="Seleccionar" 
						android:onClick="seleccionarImagen3"/>

					<Button
						android:id="@+id/bt_enviar"
						android:layout_width="wrap_content"
						android:layout_height="wrap_content"
						android:layout_alignParentBottom="true"
						android:layout_centerHorizontal="true"
						android:layout_marginBottom="36dp"
						android:onClick="enviarImagenes"
						android:text="Enviar" />
				
			

4. Clase SubirArchivoHttp

Esta clase hereda de AsyncTask, que sirve para definir una tarea que se ejecuta parte en un hilo secundario (método doInBackground) y parte en el hilo principal (otros métodos como onPrexecute y onPostexecute), esto con la finalidad de llevar a acabo un procedimiento que podría tardar mucho tiempo (como la conexión a un servidor) sin bloquear la interfaz de usuario y teniendo un control sencillo sobre dicho precedimiento. Su definición está dada por: AsyncTask <Parametros, Progreso, Resultado> Params: Datos que pasaremos al comenzar la tarea Progress: Parámetros que necesitaremos para actualizar la UI. Result: Dato que devolveremos una vez terminada la tarea. Al heredar de AsyncTask se deben sobrescribir principalmente tres métodos:

  • onPreExecute(): En este método tenemos que realizar los trabajos previos a la ejcución de la tarea. Se utiliza normalmente para configurar la tarea y para mostrar en el la interfaz de usuario que empieza la tarea.
  • doInBackground(Parametros...): Es llamado cuando termina onPreExecute(). Es la parte más importante donde tenemos que realizar la tarea propiamente dicha. Es el único método de los cuatro que no se ejecuta en el hilo del interfaz de usuario. Lo va a hacer en un hilo nuevo creado para este propósito.
  • onPostExecute(Result): Este método se usa para mostrar en el interfaz de usuario el resultado de la tarea. El parámetro de entrada (de la clase Result) corresponde con el objeto devuelto por el método doInBackground()

Sabiendo lo anterior, esta clase queda de la siguiente manera, tomando en cuenta que la URL_SERVIDOR será la del servidor que se haya implementado.

				
					public class SubirArchivoHttp extends AsyncTask<String, Void, String>{
	 
						private static String URL_SERVIDOR = "http://192.168.1.65:8080/ServidorArchivos/SubirArchivos"; 
						
						@Override
						protected void onPreExecute() {
							super.onPreExecute();
						}
						
						@Override
						protected void onPostExecute(String result) {
							super.onPostExecute(result);
						}

						@Override
						protected String doInBackground(String... arg0) {
									
							String nombre_usuario = arg0[0];
							
							try {
								//Agregando usuario
								String url_completa = URL_SERVIDOR+"?usuario="+nombre_usuario;
								
								//Creando cliente http
								HttpClient httpclient = new DefaultHttpClient();
								HttpPost httppost = new HttpPost(url_completa);
								
								//Creando entidad multiparte
								MultipartEntityBuilder builder = MultipartEntityBuilder.create();        
								builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
								
								//Agregando archivos
								for (int i=1; i<arg0.length; i++){
									File file = new File(arg0[i]);
									FileBody fileBody = new FileBody(file);
									builder.addPart("uploadFile"+i, fileBody);
								} 
								
								//Construyendo entidad http con la entidad multiparte
								HttpEntity entity = builder.build();
								httppost.setEntity(entity);
								
								//Obteniendo respuesta del servidor
								HttpResponse response = httpclient.execute(httppost);
								httpclient.getConnectionManager().shutdown();
								if (response.getStatusLine().getStatusCode() == 200){//Status = OK
									return ("Archivos enviados correctamente");
								}
								else{
									return ("Error al subir archivos: servidor");
								}
								
								
							} catch (Exception e) {
								e.printStackTrace();
								return ("Error al subir archivos: excepcion");
							}
						}
						
					}
				
			

5. Clase principal MainActivity

Se definien algunos parámetros, primero estan las constantes para identificar el número de imagen seleccionada y luego están las rutas de cada imagen

				
				private static int CODE_IMAGEN1 = 0;
				private static int CODE_IMAGEN2 = 1;
				private static int CODE_IMAGEN3 = 2;

				private String path_imagen1 = null;
				private String path_imagen2 = null;
				private String path_imagen3 = null;
				
			

Para cada botón se define una función, la cual debe tener el nombre del valor de la propiedad onClick del botón en el main.xml. Las tres funciones son prácticamente iguales, solo cambia el número de la imagen, por lo que solo se muestr la primera a continuación.

				
				public void seleccionarImagen2 (View view){
					//Intent a galeria de imagenes para seleccionar una
					Intent intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
					startActivityForResult(intent, CODE_IMAGEN2);
				}
				
			

Para obtener el resultado del Intent a la galería de imagenes se sobrescribe la siguiente función

				
				@Override 
				protected void onActivityResult(int requestCode, int resultCode, Intent data) {
					
					if (resultCode == RESULT_OK) {
						
						//Se obtiene la uri de la imagen seleccionada
						Uri selectedImage = data.getData();
						String ruta = getPath(selectedImage);//selectedImage.getPath();
						
						//Se coloca en el Textview que corresponda
						switch (requestCode)
						{
						case 0:
							TextView tv_imagen1 = (TextView)findViewById(R.id.tv_imagen1);
							tv_imagen1.setText(ruta);
							path_imagen1 = ruta;
							break;
						case 1:
							TextView tv_imagen2 = (TextView)findViewById(R.id.tv_imagen2);
							tv_imagen2.setText(ruta);
							path_imagen2 = ruta;
							break;
						case 2:
							TextView tv_imagen3 = (TextView)findViewById(R.id.tv_imagen3);
							tv_imagen3.setText(ruta);
							path_imagen3 = ruta;
							break;
						
						}
					}
					
				}
				
			

En ayuda a la función anterior, para obtener la ruta correcta de la imagen seleccionada, se implementa la siguiente función.

				
				public String getPath(Uri uri) {
				
					//Valisar que uri no este vacia
					if( uri == null ) {
						return null;
					}
					
					//Obtener path completo de la imagen seleccionada
					String[] projection = { MediaStore.Images.Media.DATA };
					Cursor cursor = getContentResolver().query(uri, projection, null, null, null);
					if( cursor != null ){
						int column_index = cursor
						.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
						cursor.moveToFirst();
						return cursor.getString(column_index);
					}
					return uri.getPath();
				}
				
			

Finalmente para llamar a la tarea asincrona que enviará las imagenes al servidor, se implementa la función del botón Enviar (parámetro OnClick).

				
				public void enviarImagenes (View view){
				
					//Validamos si todos los archivos han sido seleccionados 
					if(path_imagen1!=null && path_imagen2!=null && path_imagen3!=null){
						
						//Ejecutando tarea asincrona que envia los archivos al servidor
						SubirArchivoHttp subirImagenes = new SubirArchivoHttp();
						subirImagenes.execute("usuario", path_imagen1, path_imagen2, path_imagen3);
						
						//Obteniendo resultado de la tarea
						String resultado = null;
						try {
							resultado = subirImagenes.get();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						} catch (ExecutionException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						Toast.makeText(getApplicationContext(), "Resultado: "+resultado, Toast.LENGTH_SHORT).show();
					}
					
					//Si no pedir al usuario que los seleccione
					else{
						Toast.makeText(getApplicationContext(), "Seleccione los tres archivos", Toast.LENGTH_SHORT).show();
					}
					
				}
				
			

6. Resultado

7. Conclusión

Como se puede ver, es posible subir archivos a un servidor desde Android de manera sencilla con ayuda de algunas librerías y es realmente útil si requerimos subir imagenes, documentos o archivos de audio a un servidor que puede darles un procesamiento o simplemente almacenarlos para no saturar el teléfono de información ya que no cuenta con gran capacidad de almacenamiento. Pongo a su disposición el código del proyecto en Android que se realizó en Eclipse ADT, y el proyecto del servidor para probarlo que es un servlet simple realizado en netbeans.

Espero les sea de ayuda.

 

Para cualquier duda o mayor información, nos pueden encontrar en info(at)baware.com.mx, o a cmora(at)baware.com.mx

Saludos.

Log in