Cargue un archivo a través de un formulario HTTP, a través de MultipartEntityBuilder, con una barra de progreso

La versión cortaorg.apache...MultipartEntity está en desuso, y su actualización, MultipartEntityBuilder , aparece subrepresentada en nuestros foros en línea. Arreglemos eso. ¿Cómo se registra una callback, por lo que mi aplicación (Android) puede mostrar una barra de progreso cuando carga un archivo?

La versión larga : aquí está el “ejemplo simple de falta de suciedad” de MultipartEntityBuilder :

 public static void postFile(String fileName) throws Exception { // Based on: https://stackoverflow.com/questions/2017414/post-multipart-request-with-android-sdk HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(SERVER + "uploadFile"); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); builder.addPart("file", new FileBody(new File(fileName))); builder.addTextBody("userName", userName); builder.addTextBody("password", password); builder.addTextBody("macAddress", macAddress); post.setEntity(builder.build()); HttpResponse response = client.execute(post); HttpEntity entity = response.getEntity(); // response.getStatusLine(); // CONSIDER Detect server complaints entity.consumeContent(); client.getConnectionManager().shutdown(); } // FIXME Hook up a progress bar! 

Necesitamos arreglar ese FIXME . (Un beneficio adicional sería la carga interrumpible.) Pero (corrígeme si estoy equivocado o no), todos los ejemplos en línea parecen ser insuficientes.

Este, http://pastebin.com/M0uNZ6SB , por ejemplo, carga un archivo como “binary / octet-stream”; no es un “multipart / form-data”. Requiero campos reales

Este ejemplo, Carga de archivos con Java (con barra de progreso) , muestra cómo sobrescribir la *Entity o la *Stream . Entonces, ¿puedo decirle a MultipartEntityBuilder que .create() una entidad anulada que .create() su progreso de carga?

Por lo tanto, si deseo anular algo y reemplazar la secuencia integrada por una secuencia de conteo que envía una señal por cada 1000 bytes, tal vez pueda extender la parte FileBody y anular su getInputStream y / o writeTo .

Pero cuando pruebo la class ProgressiveFileBody extends FileBody {...} , obtengo el infame java.lang.NoClassDefFoundError .

Entonces, mientras voy de espeleología alrededor de mis archivos .jar , buscando el Def que falta, ¿alguien puede verificar mis cálculos y quizás señalar una solución más simple que he pasado por alto?

El código ganador (en espectacular estilo Java-Heresy ™) es:

 public static String postFile(String fileName, String userName, String password, String macAddress) throws Exception { HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(SERVER + "uploadFile"); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); final File file = new File(fileName); FileBody fb = new FileBody(file); builder.addPart("file", fb); builder.addTextBody("userName", userName); builder.addTextBody("password", password); builder.addTextBody("macAddress", macAddress); final HttpEntity yourEntity = builder.build(); class ProgressiveEntity implements HttpEntity { @Override public void consumeContent() throws IOException { yourEntity.consumeContent(); } @Override public InputStream getContent() throws IOException, IllegalStateException { return yourEntity.getContent(); } @Override public Header getContentEncoding() { return yourEntity.getContentEncoding(); } @Override public long getContentLength() { return yourEntity.getContentLength(); } @Override public Header getContentType() { return yourEntity.getContentType(); } @Override public boolean isChunked() { return yourEntity.isChunked(); } @Override public boolean isRepeatable() { return yourEntity.isRepeatable(); } @Override public boolean isStreaming() { return yourEntity.isStreaming(); } // CONSIDER put a _real_ delegator into here! @Override public void writeTo(OutputStream outstream) throws IOException { class ProxyOutputStream extends FilterOutputStream { /** * @author Stephen Colebourne */ public ProxyOutputStream(OutputStream proxy) { super(proxy); } public void write(int idx) throws IOException { out.write(idx); } public void write(byte[] bts) throws IOException { out.write(bts); } public void write(byte[] bts, int st, int end) throws IOException { out.write(bts, st, end); } public void flush() throws IOException { out.flush(); } public void close() throws IOException { out.close(); } } // CONSIDER import this class (and risk more Jar File Hell) class ProgressiveOutputStream extends ProxyOutputStream { public ProgressiveOutputStream(OutputStream proxy) { super(proxy); } public void write(byte[] bts, int st, int end) throws IOException { // FIXME Put your progress bar stuff here! out.write(bts, st, end); } } yourEntity.writeTo(new ProgressiveOutputStream(outstream)); } }; ProgressiveEntity myEntity = new ProgressiveEntity(); post.setEntity(myEntity); HttpResponse response = client.execute(post); return getContent(response); } public static String getContent(HttpResponse response) throws IOException { BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); String body = ""; String content = ""; while ((body = rd.readLine()) != null) { content += body + "\n"; } return content.trim(); } # NOTE ADDED LATER: as this blasterpiece gets copied into various code lineages, # The management reminds the peanut gallery that "Java-Heresy" crack was there # for a reason, and (as commented) most of that stuff can be farmed out to off- # the-shelf jar files and what-not. That's for the java lifers to tool up. This # pristine hack shall remain obviousized for education, and for use in a pinch. # What are the odds?? 

No puedo agradecerle a Phlip lo suficiente por esa solución. Aquí están los toques finales para agregar su soporte de barra de progreso. Lo ejecuté dentro de AsyncTask: el progreso a continuación le permite publicar el progreso en un método en AsyncTask que invoca AsyncTask.publishProgress () para su clase que se ejecuta en AsyncTask. La barra de progreso no es exactamente lisa, pero al menos se mueve. En un Samsung S4 cargando un archivo de imagen de 4MB después del preámbulo, estaba moviendo trozos de 4K.

  class ProgressiveOutputStream extends ProxyOutputStream { long totalSent; public ProgressiveOutputStream(OutputStream proxy) { super(proxy); totalSent = 0; } public void write(byte[] bts, int st, int end) throws IOException { // FIXME Put your progress bar stuff here! // end is the amount being sent this time // st is always zero and end=bts.length() totalSent += end; progress.publish((int) ((totalSent / (float) totalSize) * 100)); out.write(bts, st, end); } 

antes que nada: muchas gracias por la pregunta / respuesta original. Como HttpPost ahora está en desuso, lo reelaboré un poco, usando información adicional de este artículo e hice una micro biblioteca de la misma: https://github.com/licryle/HTTPPoster

Envuelve el todo en una tarea ASync; usa MultipartEntityBuilder & HttpURLConnection y escucha las devoluciones de llamada.

Usar:

  1. Descargar y extraer
  2. En su archivo de módulo build.gradle, agregue la dependencia:
 dependencies { compile project(':libs:HTTPPoster') } 
  1. Necesitas una clase para implementar la interfaz HttpListener para que puedas escuchar las devoluciones de llamada. Tiene cuatro devoluciones de llamada en HTTPListener :

    • onStartTransfer
    • en progreso
    • onFailure
    • onResponse
  2. Configure ASyncTask y ejecútelo. Aquí hay un uso rápido:

 HashMap mArgs = new HashMap<>(); mArgs.put("lat", "40.712784"); mArgs.put("lon", "-74.005941"); ArrayList aFileList = getMyImageFiles(); HttpConfiguration mConf = new HttpConfiguration( "http://example.org/HttpPostEndPoint", mArgs, aFileList, this, // If this class implements HttpListener null, // Boundary for Entities - Optional 15000 // Timeout in ms for the connection operation 10000, // Timeout in ms for the reading operation ); new HttpPoster().execute(mConf); 

Espero que pueda ayudar 🙂 ¡Siéntase libre de sugerir mejoras! Es muy reciente, y lo extiendo como lo necesito.

Aclamaciones