Upload file via HTTP form via MultipartEntityBuilder with progress bar

the short version is org.apache...MultipartEntity deprecated, and its update MultipartEntityBuilder is underrepresented in our online forums. Let me fix it. How to register a callback, so my application (Android) can display a progress bar when it loads?

long version . Here is the "missing simple example" 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! 

We need to fix this FIXME . (An additional benefit would be to interrupt the download.) But (please correct me, am I not mistaken or not), all online examples seem short.

This http://pastebin.com/M0uNZ6SB , for example, downloads the file as a "binary / octet stream"; not "multipart / form-data". I need real fields.

In this example, Downloading a Java file (with a progress bar) shows how to override *Entity or *Stream . Maybe I can tell MultipartEntityBuilder to .create() overridden object that measures its load level?

So, if I want to redefine something and replace the built-in stream with a counting stream that sends a signal for every 1000 bytes, maybe I can expand the FileBody part and redefine it with getInputStream and / or writeTo .

But when I try class ProgressiveFileBody extends FileBody {...} , I get the infamous java.lang.NoClassDefFoundError .

So while I look through the .jar files, looking for the missing Def, can someone check my math and maybe point to a simpler fix that I missed?

+44
android file-upload
Sep 23 '13 at 16:27
source share
3 answers

Winning code (in the spectacular style of Java-Heresy (tm)):

 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 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?? 
+66
04 Oct '13 at 18:12
source

I can not thank Flip for this decision. Here are the final touches to add progressbar support. I ran it inside AsyncTask - the progress below allows you to post a move back to a method in AsyncTask that calls AsyncTask.publishProgress () for your class running in AsyncTask. The progress bar is not quite even, but at least it is moving. On the Samsung S4, by downloading a 4MB image file after the preamble, it moved 4K fragments.

  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); } 
+6
Jan 17 '14 at 7:16
source

First of all: many thanks for the original question / answer. Since HttpPost is now deprecated, I reworked it a bit using the additional input of this article and made it a micro library: https://github.com/licryle/HTTPPoster

It completes the whole in the ASync task; uses MultipartEntityBuilder and HttpURLConnection and allows you to listen to callbacks.

For use:

  • Download and Extract
  • In the build.gradle Module file, add the dependency:
 dependencies { compile project(':libs:HTTPPoster') } 
  1. You need a class to implement the HttpListener interface HttpListener that you can listen to callbacks. It has four callbacks in the HttpListener :

    • onStartTransfer
    • Onprogress
    • Onfailure
    • onResponse
  2. Configure ASyncTask and run it. Here's a quick use:

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

Hope this can help :) Feel the opportunity to suggest improvements as well! This is very recent, and I am extending it as I need it.

Greetings

+2
Nov 09 '15 at 1:18
source



All Articles