multipart/form POST Upload mit Fortschrittsanzeige unmöglich?

M

mndroid

Neues Mitglied
7
Ich bin auf ein Problem gestoßen, für das es so ohne weiteres wohl keine Lösung gibt. Jedenfalls habe ich auch nach mehreren Tagen suchen in verschiedensten Foren nichts gefunden:

Es soll aus einer App heraus ein multipart/form POST Upload gemacht werden. Hierfür habe ich zwei Möglichkeiten gefunden:
1) Mit HttpURLConnection
Man baut über URL.openConnection(); eine HttpURLConnection auf, setzt die erforderlichen Parameter, erhält mit HttpURLConnection.getOutputStream() den Stream, in den man die Multipart-Daten schreiben will und durchläuft für die hochzuladene Datei eine Schleife, in der die Daten der Datei abgegriffen und mit einer Puffergröße von bspw. 1 KB in den Stream geschrieben werden.
Beispiele hierfür gibt es genügend, bspw. hier:
Yet another blog: Android: How to post file to a php server

Somit könnte man bspw. alle 1 KB den Fortschritt aktualisieren.
Das Problem: Android puffert den OutputStream auch dann, wenn man eine Content-Length mitgibt. Die Daten werden also beim Schreiben in den OutputStream noch gar nicht hochgeladen, sondern solange zwischengespeichert, bis die volle Content-Length erreicht ist. Die while-Schleife, die also eigentlich die Daten hochladen soll, ist somit in weniger als einer Sekunde durchlaufen, auch wenn es sich um eine 5MB Datei handelt. Erst mit dem Schreiben des letzten Bytes werden die Daten hochgeladen.
Die Schleife jedoch ist die einzige Stelle, an der man einen Fortschritt abgreifen kann. Somit fällt diese Möglichkeit leider raus.

2) Mit der Apache HttpClient 4
Eine weitere Möglichkeit besteht darin, zwei Bibliotheken von Apache einzubinden und den Upload mit MultipartEntity und HttpClient durchzuführen. Beispiel siehe hier:
Sam's Technical Blog: Android Multipart upload

Hier jedoch gibt es das Problem, dass man noch weniger einen Fortschritt abgreifen kann, da der Upload einzig und allein (in einem Rutsch) mit HttpClient.execute(HttpPost) durchgeführt wird und hier überhaupt kein Stream zur Verfügung steht, in dem man Schrittweise den Upload "beobachten" bzw. melden könnte.


Hat jemand eine Idee, wie man mit Android (>= 1.6) einen Multipart Upload machen UND den Fortschritt des Uploads abgreifen kann?
 
also ich kann mir nicht vorstellen, dass java / android nen buffer voll macht... ich mein, wenn du dateien die größer als dein arbeitsspeicher sind, würde das gesamte system in die kniee gehen ;) ist zwar nun ziemlich groß aber dennoch... der normale vorgang ist halt den output stream sich zu holen und da rein zu puschen und normal geht es auch Realtime... kann mir garnet vorstellen, wie es nicht direkt gesendet werden kann... also ich würde es an deiner stelle mal testen... einfach nen tcp server kurz aufbauen is ja kein akt und nen listener dazu und mal verbinden und daten zu pushen... dan siehst du es ja genau =)

Also ich würde sage Methode 1 sollte 100% gehen... welche klassen du genau benutzt, weiß ich nicht, da ich es nur von anderen Sprachen so kenne... hab es unter Android nocht nicht benötigt...

edit: eine suche von 2Sekunden!!! ;) http://stackoverflow.com/questions/254719/file-upload-with-java-with-progress-bar

gruß
Micha
 
Leider ist es aber wirklich so, ich denke mir das nicht aus... ;)
Der Stream wird nochmals intern gepuffert und erst geleert, wenn die Content-Length erreicht ist, oder aber, das hatte ich noch vergessen zu schreiben, spätestens alle 8-12MB. Das ist auch schon mehrfach in Foren berichtet worden, bspw. hier:
[android-developers] Re: Sending huge files with HttpsUrlConnection??? - AuxOne - com.googlegroups.android-developers - MarkMail

Und im Debugger sehe ich es ja auch: Die While-Schleife wird bei einem 5MB großen Foto in einer halben Sekunde durchlaufen (der komplette Inhalt wird in den Stream geschrieben. Erst mit dem letzten Bit beginnt der Upload der dann 30 Sekunden oder so dauert.

/EDIT:
Hier übrigens noch einmal die Bestätigung von offizieller Seite:
http://code.google.com/p/android/issues/detail?id=3164#c6

Wird also für die UrlConnections nach Froyo gefixed, was im Augenblick nichts bringt. Daher die Frage: Welche andere Möglichkeiten gibt es, Dateien per POST als Multipart hochzuladen und den Upload-Fortschritt abzugreifen?
 
Zuletzt bearbeitet:
@MichaelS
Den Link von Stackoverflow kannte ich schon, allerdings habe ich es bisher nie zum Laufen gebracht, da es sich allgemein auf Java bzw. speziell auf die alte HttpClient 3.1 bezieht. Diese war einmal Teil der Android SDK, wurde aber relativ früh wieder entfernt.

Ich habe es nun noch einmal mit der 4er Version der HttpClient versucht und eine Mischung aus den Wrappern aus dem ersten und dem letzten Post bei Stackoverflow. Dies hat funktioniert und ich kann nun endlich einen Progress bei Multipart Uploads anzeigen lassen.

Der unschöne Nebeneffekt ist jedoch: Da in der aktuellen Android SDK die HttpClient nur unvollständig übernommen wurde (es fehlen Multipart-Fähigkeiten) muss ich zwei Bibliotheken (apache-mime4j-0.6.jar und httpmime-4.0.1.jar) einbinden. Dadurch ist meine APK von 90KB auf 235KB angewachsen und, noch viel schlimmer, die installierte App-Größe von 175KB auf 732KB. Und das ohne wirklich neue Funktionen zu erhalten (Multipart-Uploads waren ja auch mit Mitteln der Android-SDK möglich), bzw. nur um einen Fortschritt anzeigen zu lassen.

Das ist wirklich ziemlich ärgerlich. Kennt sonst jemand vielleicht noch eine Möglichkeit, die etwas schlanker ist?

/EDIT:
Hier mal ein Beispiel (anhand der Vorlage aus dem Stackoverflow-Link) für einen Wrapper, mit dem man unter Verwendung von HttpClient 4 den Fortschritt eines Uploads abgreifen kann:
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);
}
}
}
 
Zuletzt bearbeitet:

Ähnliche Themen

Manny87
  • Manny87
Antworten
11
Aufrufe
167
swa00
swa00
S
Antworten
3
Aufrufe
637
swa00
swa00
B
Antworten
4
Aufrufe
496
bb321
B
Zurück
Oben Unten