Prześlij plik przez formularz HTTP, przez MultipartEntityBuilder, z paskiem postępu
Wersja skrócona - org.apache...MultipartEntity
jest przestarzały, a jego aktualizacja, MultipartEntityBuilder
, wydaje się niewystarczająco reprezentowana na naszych forach internetowych. Naprawmy to. Jak zarejestrować callback, aby moja (Android) aplikacja może wyświetlać pasek postępu podczas przesyłania pliku?
The long version - Oto "brakujący Brud-prosty przykład" MultipartEntityBuilder
:
public static void postFile(String fileName) throws Exception {
// Based on: https://stackoverflow.com/questions/2017414/post-multipart-request-with-android-sdk
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(SERVER + "uploadFile");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addPart("file", new FileBody(new File(fileName)));
builder.addTextBody("userName", userName);
builder.addTextBody("password", password);
builder.addTextBody("macAddress", macAddress);
post.setEntity(builder.build());
HttpResponse response = client.execute(post);
HttpEntity entity = response.getEntity();
// response.getStatusLine(); // CONSIDER Detect server complaints
entity.consumeContent();
client.getConnectionManager().shutdown();
} // FIXME Hook up a progress bar!
Musimy to naprawić FIXME
. (Dodatkową korzyścią byłoby przerywane przesyłanie.) Ale (proszę popraw mnie czy się mylę czy nie), wszystkie przykłady online wydają się niewystarczające.
Ten, http://pastebin.com/M0uNZ6SB , na przykład, przesyła plik jako "binary / octet-stream", a nie "multipart / form-data". Potrzebuję prawdziwych pól.
Ten przykład, Przesyłanie pliku za pomocą Javy (z paskiem postępu) , pokazuje, jak nadpisać *Entity
lub *Stream
. Więc może powiem MultipartEntityBuilder
.create()
nadpisanemu bytu, który mierzy jego postęp wysyłania?
Więc jeśli chcę coś zastąpić, i zastąpić wbudowany strumień ze strumieniem zliczającym, który wysyła sygnał na każde 1000 bajtów, może uda mi się rozszerzyć FileBody
część i nadpisać jej getInputStream
i/lub writeTo
.
Ale kiedy próbuję class ProgressiveFileBody extends FileBody {...}
, dostaję niesławny java.lang.NoClassDefFoundError
.
Więc kiedy będę spelunkować po moich plikach .jar
, szukając brakującego Def, czy ktoś może sprawdzić moją matmę i może wskazać prostszą poprawkę, którą przeoczyłem?
3 answers
Zwycięski kod (w stylu Java-Herezja (tm)) to:
public static String postFile(String fileName, String userName, String password, String macAddress) throws Exception {
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost(SERVER + "uploadFile");
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
final File file = new File(fileName);
FileBody fb = new FileBody(file);
builder.addPart("file", fb);
builder.addTextBody("userName", userName);
builder.addTextBody("password", password);
builder.addTextBody("macAddress", macAddress);
final HttpEntity yourEntity = builder.build();
class ProgressiveEntity implements HttpEntity {
@Override
public void consumeContent() throws IOException {
yourEntity.consumeContent();
}
@Override
public InputStream getContent() throws IOException,
IllegalStateException {
return yourEntity.getContent();
}
@Override
public Header getContentEncoding() {
return yourEntity.getContentEncoding();
}
@Override
public long getContentLength() {
return yourEntity.getContentLength();
}
@Override
public Header getContentType() {
return yourEntity.getContentType();
}
@Override
public boolean isChunked() {
return yourEntity.isChunked();
}
@Override
public boolean isRepeatable() {
return yourEntity.isRepeatable();
}
@Override
public boolean isStreaming() {
return yourEntity.isStreaming();
} // CONSIDER put a _real_ delegator into here!
@Override
public void writeTo(OutputStream outstream) throws IOException {
class ProxyOutputStream extends FilterOutputStream {
/**
* @author Stephen Colebourne
*/
public ProxyOutputStream(OutputStream proxy) {
super(proxy);
}
public void write(int idx) throws IOException {
out.write(idx);
}
public void write(byte[] bts) throws IOException {
out.write(bts);
}
public void write(byte[] bts, int st, int end) throws IOException {
out.write(bts, st, end);
}
public void flush() throws IOException {
out.flush();
}
public void close() throws IOException {
out.close();
}
} // CONSIDER import this class (and risk more Jar File Hell)
class ProgressiveOutputStream extends ProxyOutputStream {
public ProgressiveOutputStream(OutputStream proxy) {
super(proxy);
}
public void write(byte[] bts, int st, int end) throws IOException {
// FIXME Put your progress bar stuff here!
out.write(bts, st, end);
}
}
yourEntity.writeTo(new ProgressiveOutputStream(outstream));
}
};
ProgressiveEntity myEntity = new ProgressiveEntity();
post.setEntity(myEntity);
HttpResponse response = client.execute(post);
return getContent(response);
}
public static String getContent(HttpResponse response) throws IOException {
BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
String body = "";
String content = "";
while ((body = rd.readLine()) != null)
{
content += body + "\n";
}
return content.trim();
}
# NOTE ADDED LATER: as this blasterpiece gets copied into various code lineages,
# The management reminds the peanut gallery that "Java-Heresy" crack was there
# for a reason, and (as commented) most of that stuff can be farmed out to off-
# the-shelf jar files and what-not. That's for the java lifers to tool up. This
# pristine hack shall remain obviousized for education, and for use in a pinch.
# What are the odds??
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-30 04:34:41
Nie mogę podziękować Phlipowi za to rozwiązanie. Oto ostatnie poprawki do dodawania wsparcia progresbar. Uruchomiłem go wewnątrz AsyncTask - postęp poniżej pozwala opublikować postęp z powrotem do metody w AsyncTask, który wywołuje AsyncTask.publishProgress () dla twojej klasy działającej w Asynctasku. Pasek postępu nie jest dokładnie gładki, ale przynajmniej się porusza. Na Samsungu S4 wgrywanie 4MB imagefile po preambuły to było przenoszenie 4K kawałki.
class ProgressiveOutputStream extends ProxyOutputStream {
long totalSent;
public ProgressiveOutputStream(OutputStream proxy) {
super(proxy);
totalSent = 0;
}
public void write(byte[] bts, int st, int end) throws IOException {
// FIXME Put your progress bar stuff here!
// end is the amount being sent this time
// st is always zero and end=bts.length()
totalSent += end;
progress.publish((int) ((totalSent / (float) totalSize) * 100));
out.write(bts, st, end);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-17 07:16:42
Po pierwsze: wielkie dzięki za oryginalne pytanie/odpowiedź. Ponieważ HttpPost jest teraz przestarzały, przerobiłem go trochę, używając dodatkowego wejścia z tego artykułu i zrobiłem z niego mikro bibliotekę: https://github.com/licryle/HTTPPoster
Owija całość w zadanie asynchroniczne; używa MultipartEntityBuilder & HttpURLConnection i pozwala słuchać wywołań zwrotnych.
Do użycia:
- Pobierz i wyodrębnij
- w Twojej budowie.moduł gradle Plik, Dodaj zależność:
dependencies
{
compile project(':libs:HTTPPoster')
}
-
Potrzebujesz klasy do zaimplementowania interfejsu
HttpListener
, abyś mógł słuchać wywołań zwrotnych. Posiada cztery wywołania wHTTPListener
:- onStartTransfer
- onProgress
- onFailure
- onResponse
-
Skonfiguruj ASyncTask i uruchom go. Oto szybkie użycie:
HashMap<String, String> mArgs = new HashMap<>();
mArgs.put("lat", "40.712784");
mArgs.put("lon", "-74.005941");
ArrayList<File> aFileList = getMyImageFiles();
HttpConfiguration mConf = new HttpConfiguration(
"http://example.org/HttpPostEndPoint",
mArgs,
aFileList,
this, // If this class implements HttpListener
null, // Boundary for Entities - Optional
15000 // Timeout in ms for the connection operation
10000, // Timeout in ms for the reading operation
);
new HttpPoster().execute(mConf);
Mam nadzieję, że to może pomóc :) Zapraszam również do sugerowania ulepszeń! To bardzo niedawno, a ja przedłużam tak, jak tego potrzebuję.
Cheers
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-09 01:18:19