2010-07-09 10 views
17

Ich entwickle eine Android-App, die es dem Benutzer ermöglicht, eine Datei auf Dienste wie Twitpic und andere hochzuladen. Der POST-Upload erfolgt ohne externe Bibliotheken und funktioniert problemlos. Mein einziges Problem ist, dass ich keinen Fortschritt erfassen kann, weil das ganze Hochladen erfolgt, wenn ich die Antwort erhalte, während die Bytes in den Ausgabestrom schreibt. Hier ist, was ich tue:Fortschritt beim Hochladen von HTTP POST-Dateien nicht möglich (Android)

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()); 

Dann schreibe ich die Formulardaten an dos, die hier nicht so wichtig ist, jetzt. Danach schreibe ich die Dateidaten selbst (das Lesen von „in“, die die Input der Daten ich senden wollen):

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); 
} 

Danach habe ich die mehrteilige Formulardaten, um das Ende des Sende Datei. Dann schließe ich die Ströme:

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

Das alles funktioniert völlig in Ordnung, kein Problem so weit. Mein Problem ist jedoch, dass der gesamte Prozess bis zu diesem Punkt ungefähr eine oder zwei Sekunden dauert, egal wie groß die Datei ist. Der Upload selbst scheint zu passieren, wenn ich die Antwort lauten:

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

Dies dauert einige Sekunden oder Minuten, je nachdem, wie groß die Datei ist und wie schnell die Internetverbindung. Meine Fragen sind jetzt: 1) Warum passiert der Uplaod nicht, wenn ich die Bytes nach "dos" schreibe? 2) Wie kann ich einen Fortschritt erfassen, um während des Uploads einen Fortschrittsdialog zu zeigen, wenn alles auf einmal passiert?

/EDIT: 1) ich den Content-Length im Header, die das Problem etwas ändern gesetzt, aber nicht in irgendeine Weise lösen: Jetzt wird der gesamte Inhalt nach dem letzten Byte hochgeladen in geschrieben der Strom. Das ändert nichts an der Situation, dass Sie den Fortschritt nicht erfassen können, da die Daten auf einmal geschrieben werden. 2) Ich versuchte MultipartEntity in Apache HttpClient v4. Dort haben Sie überhaupt keinen OutputStream, weil alle Daten geschrieben werden, wenn Sie die Anfrage ausführen. Es gibt also keine Möglichkeit, einen Fortschritt zu machen.

Gibt es jemanden da draußen, der eine andere Idee hat, wie man einen Prozess in einem mehrteiligen/Formular-Upload erhält?

+0

Vielleicht diese Antwort werden Sie einige Hinweise geben: http://stackoverflow.com/questions/254719/file-upload-with-java-with-progress-bar/470047#470047 – pableu

+0

@pableu : Ich habe das untersucht. Das Problem ist: Es gibt keine RequestEntity im Android SDK. Es ist Teil von Apache HttpClient 3.1, der von Android nicht mehr verwendet wird. Jetzt habe ich sogar versucht, die neuen 4.0.1-Bibliotheken zu laden, aber ich sehe keine Möglichkeit, hier einen Fortschritt zu machen. Noch mehr Ideen? – Manuel

Antwort

20

ich viel in den letzten Tagen recht haben versucht, und ich denke, ich die Antwort auf die erste Frage haben:

Es ist nicht möglich, einen Fortschritt mit HttpURLConnection greifen, weil es einen Fehler/ungewöhnliches Verhalten in Android ist: http://code.google.com/p/android/issues/detail?id=3164#c6

Es wird Post Froyo befestigt werden, die man nicht etwas warten kann ...

Also, die einzige andere Option, die ich den Apache Httpclient zu verwenden, zu finden ist. Die answer linked by papleu ist korrekt, bezieht sich jedoch auf allgemeines Java. Die dort verwendeten Klassen sind in Android nicht mehr verfügbar (HttpClient 3.1 war einmal Teil des SDK). Also, was Sie tun können, ist das Hinzufügen der Bibliotheken von HttpClient 4 (insbesondere Apache-Mime4j-0.6.jar und httpmime-4.0.1.Glas) und eine Kombination aus der ersten Antwort (von Tuler) und die letzten Antwort (von Hamy) verwenden:

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); 
     } 
    } 
} 

Dies funktioniert mit Httpclient 4, aber der Nachteil ist, dass mein apk jetzt eine Größe von 235 kb hat (es war 90 kb, als ich den in meiner Frage beschriebenen mehrteiligen Upload verwendete) und, noch schlimmer, eine installierte App-Größe von 735 kb (ungefähr 170 kb vorher). Das ist wirklich schrecklich. Nur um beim Upload einen Fortschritt zu erzielen, ist die App jetzt mehr als 4 mal so groß wie zuvor.

+1

Über die .apk und Installationsgröße, haben Sie den ProGuard bei SourceForge ausgecheckt? Es schrumpft deine Bewerbung wieder dahin, wo du warst oder noch kleiner :) – Tobias

+1

Das ist eine fantastische Lösung! Vielen Dank für die Veröffentlichung –

+0

Sie brauchen apache-mime4j-0.6.jar nicht mehr, weil in der Version 4.1 alpha 2 ab die Abhängigkeiten für apache-mime4j-0.6.jar entfernt werden. – CSchulz

0

Es ist möglich, den Fortschritt des DefaultHttpClient zu erfassen. Die Tatsache, dass es in der Leitung bleibt

response = defaultHttpClient.execute(httpput, context); 

bis zum vollständigen Daten gesendet wird, bedeutet nicht zwangsläufig, dass Sie nicht den Fortschritt greifen. Nämlich, HttpContext ändert sich während der Ausführung dieser Zeile. Wenn Sie sich also über einen anderen Thread nähern, können Sie Änderungen daran beobachten. Was Sie brauchen, ist ConnAdapter. Es hat HttpConnectionMetrics eingebettet.

Wenn Sie dies regelmäßig überprüfen, werden Sie den Fortschritt beobachten. Achten Sie darauf, Threads korrekt zu behandeln, und alle versuchen, sich zu schützen. Behandeln Sie insbesondere den Fall, wenn der Kontext nicht mehr gültig ist (IllegalStateException), der auftritt, wenn Put abgebrochen oder abgeschlossen wird.

0

Verwenden Sie stattdessen WatchedInputStream. Es ist schön, großartig und einfach zu bedienen.

  1. Verwenden Sie WatchedInputStream, um den Eingabestream zu umbrechen.
  2. watchedStream.setListener

    watchedStream.setListener (neu WatchedInputStream.Listener() {

    public void notify (int lesen) {// etwas tun UI zu aktualisieren.
    } }

1

Probieren Sie dies aus. Es funktioniert.

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); 
    } 

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

Verwandte Themen