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

Estoy desarrollando una aplicación de Android que permite al usuario subir un archivo a servicios como Twitpic y otros. La carga 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 realiza cuando recibo la respuesta, no mientras escribo los bytes en la salida de salida. 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 en dos, lo cual no es tan importante aquí, ahora. Después de eso, escribo los datos del archivo en sí (leyendo desde “en”, que es el InputStream de los datos que deseo 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 del formulario multiparte para indicar el final del archivo. Luego, cierro las transmisiones:

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

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

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

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

/ EDITAR: 1) Configuré Content-Length en el encabezado, lo que cambia el problema un poco, pero no lo resuelve de ninguna manera: ahora todo el contenido se carga después de que el último byte se haya escrito en la transmisión. Por lo tanto, esto no cambia la situación en la que no puede tomar el progreso, porque una vez más, los datos se escriben a la vez. 2) Probé MultipartEntity en Apache HttpClient v4. Allí no tiene un OutputStream en absoluto, porque todos los datos se escriben cuando realiza la solicitud. Entonces, de nuevo, no hay forma de lograr un progreso.

¿Hay alguien por ahí que tenga alguna otra idea de cómo tomar un proceso en una carga multiparte / formulario?

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

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

Se arreglará después de Froyo, que no es algo que uno pueda esperar …

Entonces, la única otra opción que encontré es usar Apache HttpClient. La respuesta vinculada por papleu es correcta, pero se refiere a Java general. Las clases que se usan allí ya no están disponibles en Android (HttpClient 3.1 fue parte del SDK, una vez). Entonces, lo que puedes hacer es agregar las bibliotecas de HttpClient 4 (específicamente apache-mime4j-0.6.jar y httpmime-4.0.1.jar) y usar 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 el inconveniente es que mi apk ahora tiene un tamaño de 235 kb (era 90 kb cuando utilicé la carga de varias partes descrita en mi pregunta) y, lo que es peor, un tamaño de aplicación instalada de 735 kb (aproximadamente 170 kb antes). Eso es realmente horrible Solo para obtener un progreso durante la carga, el tamaño de la aplicación ahora es más de 4 veces mayor 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 del DefaultHttpClient. El hecho de que se mantenga en la línea

 response = defaultHttpClient.execute( httpput, context ); 

hasta que se envíen los datos completos, esto no significa necesariamente que no puede captar el progreso. A saber, HttpContext cambia al ejecutar esta línea, por lo que si se acerca a través de un subproceso diferente, puede observar cambios en eso. Lo que necesitas es ConnAdapter. Tiene HttpConnectionMetrics embedded

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

Si revisas esto periódicamente, observarás el progreso. Asegúrese de manejar correctamente los hilos, y todos intenten / capturen protegidos. Especialmente, maneje el caso cuando Contexto ya no sea válido (IllegalStateException), que ocurre cuando se cancela o se completa Put.

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

  1. usa WatchedInputStream para ajustar tu inputstream.
  2. watchedStream.setListener

    watchedStream.setListener (nuevo WatchedInputStream.Listener () {

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