Загрузите файл через HTTP-форму через MultipartEntityBuilder с индикатором выполнения

Краткая версияorg.apache...MultipartEntity устарела, и ее обновление, MultipartEntityBuilder , оказывается MultipartEntityBuilder на наших онлайн-форумах. Давайте исправим это. Как регистрировать обратный вызов, поэтому мое приложение (Android) может отображать индикатор выполнения, когда он загружает файл?

Длинная версия. Вот «Простой простой пример» для 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! 

Мы должны исправить это FIXME . (Дополнительным преимуществом будет прерывание загрузки.) Но (пожалуйста, поправьте меня, ошибаюсь я или нет), все онлайн-примеры, похоже, не оправдались.

Например, этот http://pastebin.com/M0uNZ6SB загружает файл как «двоичный / октетный поток»; А не «multipart / form-data». Мне нужны реальные поля.

В этом примере загрузка файла с Java (с индикатором выполнения) показывает, как переопределить *Entity или *Stream . Поэтому, возможно, я могу сказать MultipartEntityBuilder для .create() переопределенного объекта, который .create() его уровень загрузки?

Поэтому, если я хочу переопределить что-то и заменить встроенный поток потоком подсчета, который отправляет сигнал на каждые 1000 байтов, возможно, я могу расширить часть FileBody и переопределить его getInputStream и / или writeTo .

Но когда я пытаюсь class ProgressiveFileBody extends FileBody {...} , я получаю печально известный java.lang.NoClassDefFoundError .

Итак, пока я иду вокруг своих файлов .jar , ища недостающую Def, может кто-то проверить мою математику, и, возможно, указать на более простое исправление, которое я упустил?

Выигрышный код (в эффектном стиле Java-Heresy ™):

 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?? 

Не могу поблагодарить Флип для этого решения. Вот последние штрихи для добавления поддержки progressbar. Я запустил его внутри AsyncTask. Прогресс ниже позволяет вам опубликовать прогресс в методе в AsyncTask, который вызывает AsyncTask.publishProgress () для вашего класса, запущенного в AsyncTask. Индикатор выполнения не совсем ровный, но, по крайней мере, он движется. На Samsung S4, загрузив 4MB-файл изображения после преамбулы, он двигал 4K кусками.

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

В первую очередь: огромное спасибо за оригинальный вопрос / ответ. Поскольку HttpPost теперь устарел, я немного переработал его, используя дополнительный ввод из этой статьи и сделал его микро-библиотеку: https://github.com/licryle/HTTPPoster

Он завершает целое в задаче ASync; Использует MultipartEntityBuilder & HttpURLConnection, и давайте послушаем обратные вызовы.

Использовать:

  1. Загрузить и извлечь
  2. В файле build.gradle Module добавьте зависимость:
 dependencies { compile project(':libs:HTTPPoster') } 
  1. Вам нужен класс для реализации интерфейса HttpListener чтобы вы могли прослушивать обратные вызовы. Он имеет четыре обратных вызова в HTTPListener :

    • onStartTransfer
    • в процессе
    • OnFailure
    • onResponse
  2. Настройте ASyncTask и запустите его. Вот быстрое использование:

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

Надеюсь, что это может помочь 🙂 Чувствуйте себя также свободно предлагать улучшения! Это очень недавно, и я расширяю его, поскольку мне это нужно.

ура