Télécharger un fichier via un formulaire HTTP, via MultipartEntityBuilder, avec une barre de progression

La version courteorg.apache...MultipartEntity est obsolète, et sa mise à niveau, MultipartEntityBuilder , apparaît sous-représentée dans nos forums en ligne. Corrigeons ça. Comment peut-on enregistrer un rappel afin que mon application (Android) puisse afficher une barre de progression lors du téléchargement d’un fichier?

La version longue – Voici l’exemple “manquant de simplicité” de MultipartEntityBuilder :

 public static void postFile(Ssortingng 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! 

Nous devons réparer ce FIXME . (Un avantage supplémentaire serait les téléchargements interruptibles.) Mais (corrigez-moi, je me trompe ou non), tous les exemples en ligne semblent manquer.

Celui-ci, http://pastebin.com/M0uNZ6SB , par exemple, télécharge un fichier en tant que “binary / octet-stream”; pas un “multipart / form-data”. J’ai besoin de vrais champs.

Cet exemple, File Upload with Java (avec barre de progression) , montre comment remplacer l’ *Entity ou le *Stream . Alors peut-être que je peux dire au MultipartEntityBuilder de .create() une entité .create() qui mesure sa progression de téléchargement?

Donc, si je veux remplacer quelque chose et remplacer le stream intégré par un stream de comptage qui envoie un signal tous les 1 000 octets, je peux peut-être étendre la partie FileBody et remplacer son getInputStream et / ou writeTo .

Mais quand j’essaie la class ProgressiveFileBody extends FileBody {...} , je reçois le sortingstement célèbre java.lang.NoClassDefFoundError .

Alors, pendant que je traite mes fichiers .jar , à la recherche de la définition de défaut manquante, est-ce que quelqu’un peut vérifier mes calculs et peut-être indiquer un correctif plus simple que j’ai omis?

Le code gagnant (dans le style spectaculaire Java-Heresy ™) est:

 public static Ssortingng postFile(Ssortingng fileName, Ssortingng userName, Ssortingng password, Ssortingng 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 Ssortingng getContent(HttpResponse response) throws IOException { BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); Ssortingng body = ""; Ssortingng content = ""; while ((body = rd.readLine()) != null) { content += body + "\n"; } return content.sortingm(); } # 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?? 

Je ne peux pas remercier assez Phlip pour cette solution. Voici les touches finales pour append votre prise en charge de la barre de progression. Je l’ai exécuté dans une AsyncTask – les progrès ci-dessous vous permettent de renvoyer la progression vers une méthode dans AsyncTask qui appelle AsyncTask.publishProgress () pour votre classe s’exécutant dans AsyncTask. La barre de progression n’est pas exactement lisse mais au moins elle bouge. Sur un Samsung S4 téléchargeant un fichier image de 4 Mo après le préambule, il déplaçait des blocs de 4 Ko.

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

Tout d’abord, merci pour la question / réponse originale. Étant donné que HttpPost est maintenant obsolète, je l’ai toutefois retravaillé un peu, en utilisant des informations supplémentaires de cet article et en en créant une micro-bibliothèque: https://github.com/licryle/HTTPPoster

Il enveloppe le tout dans une tâche ASync; utilise MultipartEntityBuilder & HttpURLConnection et vous permet d’écouter les rappels.

Utiliser:

  1. Télécharger et extraire
  2. Dans votre fichier module build.gradle, ajoutez la dépendance:
 dependencies { comstack project(':libs:HTTPPoster') } 
  1. Vous avez besoin d’une classe pour implémenter l’interface HttpListener afin de pouvoir écouter les rappels. Il a quatre rappels dans HTTPListener :

    • onStartTransfer
    • onProgress
    • surFailure
    • sur réponse
  2. Configurez ASyncTask et lancez-le. Voici une utilisation rapide:

 HashMap mArgs = new HashMap<>(); mArgs.put("lat", "40.712784"); mArgs.put("lon", "-74.005941"); ArrayList 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); 

J’espère que ça peut aider 🙂 Sentez-vous également libre de proposer des améliorations! C’est très récent et je l’étends au besoin.

À votre santé