No se puede tomar el progreso en la carga de archivos POST HTTP (Android)

Estoy desarrollando una aplicación de Android que permite al usuario subir un archivo a servicios como Twitpic y otros. La carga de POST se realiza sin bibliotecas externas y funciona bien. Mi único problema es que no puedo obtener ningún progreso porque toda la carga se hace cuando recibo la respuesta, no mientras escribo los bytes en el outputtream. Esto es lo que hago:

URL url = new URL(urlString); 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()); 

Luego escribo los datos del formulario a dos, que no es tan importante aquí, ahora. Después de eso escribo los datos del archivo en sí (lectura de "en", que es el InputStream de los datos que quiero enviar):

 while ((bytesAvailable = in.available()) > 0) { bufferSize = Math.min(bytesAvailable, maxBufferSize); byte[] buffer = new byte[bufferSize]; bytesRead = in.read(buffer, 0, bufferSize); dos.write(buffer, 0, bytesRead); } 

Después de eso, envío los datos de formulario de varias partes para indicar el final del archivo. Entonces, cierro los arroyos:

 in.close(); dos.flush(); dos.close(); 

Todo esto funciona perfectamente bien, no hay problema hasta ahora. Mi problema es, sin embargo, que todo el proceso hasta este punto toma alrededor de uno o dos segundos, no importa cuán grande es el archivo. La subida en sí parece suceder cuando leo la respuesta:

 DataInputStream inStream = new DataInputStream(conn.getInputStream()); 

Esto toma varios segundos o minutos, dependiendo de cómo es grande el archivo y de cómo rápidamente la conexión del Internet. Mis preguntas ahora son: 1) ¿Por qué no ocurre el uplaod cuando escribo los bytes a "dos"? 2) ¿Cómo puedo obtener un progreso para mostrar un diálogo de progreso durante la carga cuando todo sucede a la vez?

/ EDIT: 1) Establecer el contenido de longitud en el encabezado que cambia el problema un poco, pero no de ninguna manera resolverlo: Ahora todo el contenido se carga después de que el último byte se escribe en el flujo. Por lo tanto, esto no cambia la situación que no se puede agarrar el progreso, porque de nuevo, los datos se escriben a la vez. 2) He intentado MultipartEntity en Apache HttpClient v4. Allí usted no tiene un OutputStream en absoluto, porque todos los datos se escriben cuando usted realiza la petición. Así que de nuevo, no hay manera de agarrar un progreso.

¿Hay alguien por ahí que tiene alguna otra idea de cómo agarrar un proceso en un multipart / form subir?

He intentado bastante en los últimos días y creo que tengo la respuesta a la pregunta inicial:

No es posible obtener un progreso con HttpURLConnection porque hay un error / comportamiento inusual en Android: http://code.google.com/p/android/issues/detail?id=3164#c6

Se fijará post Froyo, que no es algo que uno puede esperar …

Por lo tanto, la única otra opción que encontré es utilizar el Apache HttpClient. La respuesta vinculada por papleu es correcta, pero se refiere a Java general. Las clases que se utilizan allí no están disponibles en Android ya (HttpClient 3.1 era parte del SDK, una vez). Por lo tanto, lo que puede hacer es añadir las bibliotecas de HttpClient 4 (específicamente apache-mime4j-0.6.jar y httpmime-4.0.1.jar) y utilizar una combinación de la primera respuesta (por Tuler) y la última respuesta (por Hamy ):

 import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; public class CountingMultipartEntity extends MultipartEntity { private final ProgressListener listener; public CountingMultipartEntity(final ProgressListener listener) { super(); this.listener = listener; } public CountingMultipartEntity(final HttpMultipartMode mode, final ProgressListener listener) { super(mode); this.listener = listener; } public CountingMultipartEntity(HttpMultipartMode mode, final String boundary, final Charset charset, final ProgressListener listener) { super(mode, boundary, charset); this.listener = listener; } @Override public void writeTo(final OutputStream outstream) throws IOException { super.writeTo(new CountingOutputStream(outstream, this.listener)); } public static interface ProgressListener { void transferred(long num); } public static class CountingOutputStream extends FilterOutputStream { private final ProgressListener listener; private long transferred; public CountingOutputStream(final OutputStream out, final ProgressListener listener) { super(out); this.listener = listener; this.transferred = 0; } public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); this.transferred += len; this.listener.transferred(this.transferred); } public void write(int b) throws IOException { out.write(b); this.transferred++; this.listener.transferred(this.transferred); } } } 

Esto funciona con HttpClient 4, pero la desventaja es que mi apk ahora tiene un tamaño de 235 kb (era de 90 kb cuando usé la subida de múltiples partes descrita en mi pregunta) y, peor aún, un tamaño de aplicación instalado de 735 kb 170 kb antes). Eso es realmente horrible. Sólo para obtener un progreso durante la carga el tamaño de la aplicación es ahora más de 4 veces más grande que antes.

Probar esto. Funciona.

 connection = (HttpURLConnection) url_stripped.openConnection(); connection.setRequestMethod("PUT"); String boundary = "---------------------------boundary"; String tail = "\r\n--" + boundary + "--\r\n"; connection.addRequestProperty("Content-Type", "image/jpeg"); connection.setRequestProperty("Connection", "Keep-Alive"); connection.setRequestProperty("Content-Length", "" + file.length()); connection.setDoOutput(true); String metadataPart = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"metadata\"\r\n\r\n" + "" + "\r\n"; String fileHeader1 = "--" + boundary + "\r\n" + "Content-Disposition: form-data; name=\"uploadfile\"; filename=\"" + fileName + "\"\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Transfer-Encoding: binary\r\n"; long fileLength = file.length() + tail.length(); String fileHeader2 = "Content-length: " + fileLength + "\r\n"; String fileHeader = fileHeader1 + fileHeader2 + "\r\n"; String stringData = metadataPart + fileHeader; long requestLength = stringData.length() + fileLength; connection.setRequestProperty("Content-length", "" + requestLength); connection.setFixedLengthStreamingMode((int) requestLength); connection.connect(); DataOutputStream out = new DataOutputStream( connection.getOutputStream()); out.writeBytes(stringData); out.flush(); int progress = 0; int bytesRead = 0; byte buf[] = new byte[1024]; BufferedInputStream bufInput = new BufferedInputStream( new FileInputStream(file)); while ((bytesRead = bufInput.read(buf)) != -1) { // write output out.write(buf, 0, bytesRead); out.flush(); progress += bytesRead; // update progress bar publishProgress(progress); } // Write closing boundary and close stream out.writeBytes(tail); out.flush(); out.close(); // Get server response BufferedReader reader = new BufferedReader( new InputStreamReader(connection.getInputStream())); String line = ""; StringBuilder builder = new StringBuilder(); while ((line = reader.readLine()) != null) { builder.append(line); } 

Referencia: http://delimitry.blogspot.in/2011/08/android-upload-progress.html

Es posible tomar el progreso desde el DefaultHttpClient. El hecho de que permanezca en la línea

 response = defaultHttpClient.execute( httpput, context ); 

Hasta que se envíen los datos completos, no significa necesariamente que usted no puede agarrar el progreso. Es decir, HttpContext cambia al ejecutar esta línea, por lo que si se aborda a través de otro hilo, puede observar cambios en eso. Lo que necesita es ConnAdapter. Tiene HttpConnectionMetrics incrustado

 final AbstractClientConnAdapter connAdapter = (AbstractClientConnAdapter) context.getAttribute(ExecutionContext.HTTP_CONNECTION); final HttpConnectionMetrics metrics = connAdapter.getMetrics(); int bytesSent = (int)metrics.getSentBytesCount(); 

Si comprueba esto periódicamente, observará el progreso. Asegúrese de manejar correctamente las roscas, y todo intenta / atrapar vigilado. Especialmente, maneje el caso cuando Context ya no es válido (IllegalStateException), que ocurre cuando Put se cancela o se completa.

Utilice WatchedInputStream en su lugar. Es agradable, genial y fácil de usar.

  1. Use WatchedInputStream para envolver su inputtream.
  2. SeenStream.setListener

    SeenStream.setListener (new WatchedInputStream.Listener () {

    Public void notify (int read) {// hacer algo para actualizar la interfaz de usuario.
    }}

  • Creación de una cola de carga
  • Cargar un archivo a través de un formulario HTTP, a través de MultipartEntityBuilder, con una barra de progreso
  • MultipartEntity y Json
  • Android: Cómo deshabilitar la depuración para subir el apk
  • ¿Cómo subir videos a youtube en android?
  • El progreso de la subida de OkHttp no está sincronizado con la subida real
  • Picasso para cargar matriz de bytes
  • Android Studio nunca termina de cargar la aplicación en el Nexus 10
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.