Upload de ficheros

Para la subida de ficheros desde Android a un servidor propio son necesarias dos piezas en funcionamiento: * Un servicio de subida implementado en nuestro Android * Un servicio de recepción implementado en nuestro servidor

El segundo apartado puede ser resuelto con un script php ubicado en una url pública propia. El script que proponemos, de hecho, es derivado del propuesto en la sección Save to web del wiki de Processing, aunque con modificaciones. En particular:

// Cambio todas las apariciones de $data por $_FILES['image']['tmp_name']

y la inclusión del script de resize de imágenes encontrado aquí. El archivo SimpleImage.php tiene que guardarse en el mismo directorio que el que contiene a nuestro upload.php. Para que el upload.php funcione tendremos que crear además un directorio público (permisos 777) de nombre public por ejemplo en esa misma ruta.

Con estas modificaciones nuestro servicio de recepción queda así:

upload.php

<? 
 
// upload.php
// Derivado de http://wiki.processing.org/w/Saving_files_to_a_web_server
 
 
include('SimpleImage.php');
 
$Title = $_GET['title']; 
$Ext = $_GET['ext']; 
$folder = $_GET['folder'];
$savepath = dirname($_SERVER["PATH_TRANSLATED"]);
 
$filename = $Title.".".$Ext;
 
//while(file_exists($folder."/".$filename))
//  $filename = $Title."-".rand(2,500).".".$Ext;
 
  echo $filename . "\n";
 if (is_uploaded_file($_FILES['image']['tmp_name']))
 {
   //$newfile = $savepath."/".$folder."/".$filename;
   $newfile = $folder."/".$filename;
 
   echo $newfile . "\n";
 
   if (!copy($_FILES['image']['tmp_name'], $newfile))
   {
      // if an error occurs the file could not
      // be written, read or possibly does not exist
      echo "Error #1 Uploading File.";
      exit();
   }else{
      echo $folder."/".$filename;
 
	  $image = new SimpleImage();
	  $image->load($folder."/".$filename);
      $image->resizeToHeight(400);
      $image->save($folder."/".$filename);	  
   }
 } else {
   echo "Error #2 Uploading File.";
   exit();
 }
?>

Clase FileUpload

La forma más sencilla que hemos encontrado para un servicio de subida es la clase FileUpload que copiamos en esta sección. Para utilizarla es necesario instalar httpmime de aquí e instalarla en una carpeta code dentro del directorio de nuestro sketch.

Su empleo requiere estos pasos:

1. Declaración de un objeto encargado de todas las funciones de subida, que serán lanzadas como threads, y declaración de un número id para el identificador del Dialog que nos informará de la subida del archivo:

FileUpload fileUploader;
final int DIALOG_LOADING = 2;

2. En el setup, inicialización del uploader:

  fileUploader = new FileUpload();

3. Para lanzar el uploader:

    fileUploader.start(ruta_absoluta_del_archivo_que_queremos_subir);

4. Para la creación de un Dialog que informe de la subida:

/****************
* onCreateDialog (id)
*
* Lanzador de Dialogs (mensajes)
***/
 
Dialog onCreateDialog(int dialogId) {	
 
  if (dialogId == DIALOG_LOADING)  {  
    Dialog dialog = new ProgressDialog(this);
    dialog.setTitle("Subiendo achivo");
    ((ProgressDialog)dialog).setMessage("Por favor espere...");
    ((ProgressDialog)dialog).setIndeterminate(true);
    dialog.setCancelable(false);   
    return dialog;
  }
 
  return null;
}

código de la clase

/****************************************
* class FileUpload
*
* Muestra un Dialog con id DIALOG_LOADING
* Este id tiene que existir como global
* en el sketch principal...
****/
 
 
 
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.*;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.entity.mime.content.ByteArrayBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.NameValuePair;
 
boolean running;
 
class FileUpload extends Thread {
 
  String path;
  String nombre;  
  String extension;
 
  FileUpload() {
  }
 
 
  /****************************************
   * void start(path)
   * 
   * Este es el método que lanza el thread
   * Envía un mensaje de lanzamiento también
   * a un Dialog con id DIALOG_LOADING.
   ****/
 
  void start(String path_) {
    path = path_;
    File f = new File(path);
    nombre = stripExtension(f.getName());
    extension = getExtension(path);
    showDialog(DIALOG_LOADING);
    println(nombre + "." + extension);
 
    running = true;
    super.start();
  }
 
 
  /****************************************
   * void run()
   * 
   * Es ejecutado automáticamente por el thread
   * cuando se ejecuta el método start() de éste
   *
   * Cuando termina la ejecución, cierra el dialog
   * enviando un mensaje de cierre al dialog
   * DIALOG_LOADING
   ****/  
 
  void run() {
    if (running) {
      File f = new File(path);
      List<NameValuePair> params = new ArrayList<NameValuePair>(1);
      params.add(new BasicNameValuePair("image", f.getAbsolutePath()));
      post("http://url_del_archivo_upload.php"+nombre+"&ext="+extension+"&folder=public", params);
      dismissDialog(DIALOG_LOADING);
      running = false;
    }
  }
 
 
 
  /****************************************
   * void post(url, List nameValuePairs)
   * 
   * Envía un mensaje POST a la url con los parámetros indicados
   * Origen: http://stackoverflow.com/questions/2935946/sending-images-using-http-post
   ****/
 
  void post(String url, List<NameValuePair> nameValuePairs) {
    HttpClient httpClient = new DefaultHttpClient();
    HttpContext localContext = new BasicHttpContext();
    HttpPost httpPost = new HttpPost(url);
 
    try {
      MultipartEntity entity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE);
 
      for (int index=0; index < nameValuePairs.size(); index++) {
        if (nameValuePairs.get(index).getName().equalsIgnoreCase("image")) {
          // If the key equals to "image", we use FileBody to transfer the data
          entity.addPart(nameValuePairs.get(index).getName(), new FileBody(new File (nameValuePairs.get(index).getValue())));
        }
        else {
          // Normal string data
          entity.addPart(nameValuePairs.get(index).getName(), new StringBody(nameValuePairs.get(index).getValue()));
        }
      }
 
      httpPost.setEntity(entity);
 
      HttpResponse response = httpClient.execute(httpPost, localContext);
      println(getResponseBody(response));
    } 
 
    catch (IOException e) {
      e.printStackTrace();
    }
  }
 
 
  /****************************************
   * String getResponseBode(response),
   * String _getResponseBody(final HttpEntity entity),
   * String getContentCharset(entity)
   * Origen: http://thinkandroid.wordpress.com/2009/12/30/getting-response-body-of-httpresponse/
   *
   * Permiten leer la respuesta http del servidor 
   * Util para debug, println
   ****/
 
  String getResponseBody(HttpResponse response) {
    String response_text = null;
    HttpEntity entity = null;
    try {
      entity = response.getEntity();
      response_text = _getResponseBody(entity);
    } 
    catch (ParseException e) {
      e.printStackTrace();
    } 
    catch (IOException e) {
      if (entity != null) {
        try {
          entity.consumeContent();
        } 
        catch (IOException e1) {
        }
      }
    }
    return response_text;
  }
 
 
  String _getResponseBody(final HttpEntity entity) throws IOException, ParseException {
    if (entity == null) { 
      throw new IllegalArgumentException("HTTP entity may not be null");
    }
    InputStream instream = entity.getContent();
    if (instream == null) { 
      return "";
    }
    if (entity.getContentLength() > Integer.MAX_VALUE) { 
      throw new IllegalArgumentException(
      "HTTP entity too large to be buffered in memory");
    }
    String charset = getContentCharSet(entity);
    if (charset == null) {
      charset = HTTP.DEFAULT_CONTENT_CHARSET;
    }
    Reader reader = new InputStreamReader(instream, charset);
    StringBuilder buffer = new StringBuilder();
    try {
      char[] tmp = new char[1024];
      int l;
      while ( (l = reader.read (tmp)) != -1) {
        buffer.append(tmp, 0, l);
      }
    } 
    finally {
      reader.close();
    }
    return buffer.toString();
  }
 
 
  String getContentCharSet(final HttpEntity entity) throws ParseException {
    if (entity == null) { 
      throw new IllegalArgumentException("HTTP entity may not be null");
    }
    String charset = null;
    if (entity.getContentType() != null) {
      HeaderElement values[] = entity.getContentType().getElements();
      if (values.length > 0) {
        NameValuePair param = values[0].getParameterByName("charset");
        if (param != null) {
          charset = param.getValue();
        }
      }
    }
 
    return charset;
  }
 
 
  /****************************************
   * String stripExtension(str)
   *
   * Devuelve el filename sin extension
   ****/
 
  String stripExtension (String str) {
    // Handle null case specially.
    if (str == null) return null;
    // Get position of last '.'.
    int pos = str.lastIndexOf(".");
    // If there wasn't any '.' just return the string as is.
    if (pos == -1) return str;
    // Otherwise return the string, up to the dot.
    return str.substring(0, pos);
  }
 
 
  /****************************************
   * String getExtension(str)
   *
   * Devuelve la extension de un path o filename
   ****/
 
  String getExtension (String str) {
    // Handle null case specially.
    if (str == null) return null;
    // Get position of last '.'.
    int pos = str.lastIndexOf(".");
    // If there wasn't any '.' just return the string as is.
    if (pos == -1) return str;
    // Otherwise return the string, up to the dot.
    return str.substring(pos+1, str.length());
  }
}