No se puede agarrar el progreso en la carga de archivos http POST(Android)
upload progress (4)
Es posible tomar el progreso desde el DefaultHttpClient. El hecho de que se quede en la línea.
response = defaultHttpClient.execute( httpput, context );
Hasta que no se envíen los datos completos, esto no significa necesariamente que no pueda tomar el progreso. A saber, HttpContext cambia mientras ejecuta esta línea, por lo que si lo aborda a través de un subproceso diferente, puede observar cambios en eso. Lo que necesitas 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 verifica esto periódicamente, observará el progreso. Asegúrese de manejar correctamente las roscas, y todos los dispositivos de prueba / captura están protegidos. Especialmente, manejar el caso cuando el contexto ya no es válido (IllegalStateException), lo que ocurre cuando se cancela o se completa la colocación.
Estoy desarrollando una aplicación para Android que permite al usuario cargar 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 tomar ningún progreso porque toda la carga se realiza cuando recibo la respuesta, no mientras escribo los bytes en la secuencia 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 a dos, lo cual no es tan importante aquí, ahora. Después de eso escribo los datos del archivo (leyendo de "in", 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 del formulario de varias partes para indicar el final del archivo. Luego, cierro los arroyos:
in.close();
dos.flush();
dos.close();
Todo esto funciona perfectamente bien, no hay problema hasta ahora. Sin embargo, mi problema es que todo el proceso hasta este punto toma alrededor de uno o dos segundos, sin importar qué tan grande sea el archivo. La carga en sí parece ocurrir 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ápida es la conexión a internet. Mis preguntas ahora son: 1) ¿Por qué no se produce una subida cuando escribo los bytes en "dos"? 2) ¿Cómo puedo tomar un progreso para mostrar un diálogo de progreso durante la carga cuando todo sucede a la vez?
/ EDITAR: 1) Establecí la longitud del contenido en el encabezado que cambia un poco el problema, pero no lo resuelve de ninguna manera: ahora todo el contenido se carga después de que se escribe el último byte en la secuencia. Por lo tanto, esto no cambia la situación por la que no puede tomar el progreso, porque nuevamente, los datos se escriben a la vez. 2) Probé MultipartEntity en Apache HttpClient v4. Allí no tiene ningún OutputStream, porque todos los datos se escriben cuando realiza la solicitud. Así que de nuevo, no hay manera de agarrar un progreso.
¿Hay alguien por ahí que tenga alguna otra idea de cómo capturar un proceso en una carga de múltiples partes / formularios?
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
Será arreglado después de Froyo, que no es algo que uno pueda esperar ...
Por lo tanto, la única otra opción que encontré es usar el HttpClient de Apache. 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 puede 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 (de Tuler) y la última respuesta (de 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 (fue de 90 kb cuando usé 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 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
Utilice WatchedInputStream en su lugar. Es agradable, genial y fácil de usar.
- use WatchedInputStream para envolver su flujo de entrada.
seenStream.setListener
watchStream.setListener (nuevo WatchedInputStream.Listener () {
notificar públicamente (int leer) {// hacer algo para actualizar la interfaz de usuario.
}}