45 votes

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

La version courte - org.apache...MultipartEntity est obsolète, et sa mise à niveau, MultipartEntityBuilder, semble sous-représentée dans nos forums en ligne. Nous allons remédier à cela. Comment enregistrer un rappel, donc mon (Android) peut afficher une barre de progression comme il télécharge un fichier?

La version longue - Voici le "manque de saleté simple exemple" MultipartEntityBuilder:

public static void postFile(String fileName) throws Exception {
    // Based on: http://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 avons besoin de le corriger FIXME. (Un avantage supplémentaire serait interruptible uploads.) Mais (corrigez-moi si je me trompe), tous les exemples en ligne semblent tomber à court.

Celui-ci, http://pastebin.com/M0uNZ6SBpar exemple, télécharge un fichier en tant que "binaire/octet-stream", pas un "multipart/form-data". J'ai besoin de véritables champs.

Cet exemple, Upload de Fichier avec Java (avec barre de progression), montre comment remplacer l' *Entity ou *Stream. Alors peut-être je peux dire que l' MultipartEntityBuilder de .create() un substituée entité mètres de son avancement du téléchargement?

Donc, si je veux remplacer quelque chose, et de remplacer le flux avec un comptage de flux qui envoie un signal pour chaque tranche de 1000 octets, peut-être que je peux prolonger l' FileBody partie, et de remplacer son getInputStream et/ou writeTo.

Mais quand j'essaie class ProgressiveFileBody extends FileBody {...},- je obtenir de l'infâme java.lang.NoClassDefFoundError.

Ainsi, alors que je faire de la spéléologie autour de mon .jar fichiers, manque de Def, quelqu'un peut-il vérifier mes calculs, et peut-être à un point de plus simple correctif, j'ai négligé?

67voto

Phlip Points 2061

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

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

6voto

Mike Kogan Points 101

Je ne peux pas remercier assez Phlip pour cette solution. Voici les touches finales pour l'ajout de votre support progressbar. Je l'ai exécuté à l'intérieur d'une AsyncTask - progress ci-dessous vous permet de poster la progression dans une méthode de l'AsyncTask qui appelle AsyncTask.publishProgress () pour votre classe qui s'exécute dans l'AsyncTask. La barre de progression n'est pas parfaitement 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 morceaux de 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);
            }
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X