Domanda

Sono estremamente nuovo su Java, e per lo più mi sto insegnando da solo, quindi ho iniziato a costruire un applet. Vorrei crearne uno in grado di selezionare un file dal disco locale e caricarlo come richiesta POST multipart / form-data ma con una barra di avanzamento . Ovviamente l'utente deve concedere l'autorizzazione all'applet Java per accedere al disco rigido. Ora ho già funzionato la prima parte: l'utente può selezionare un file usando un oggetto JFileChooser , che restituisce comodamente un oggetto File . Ma mi chiedo cosa verrà dopo. So che File.length () mi darà la dimensione totale in byte del file, ma come posso inviare il File selezionato sul web e come posso monitorare quanti byte sono stati inviati? Grazie in anticipo.

È stato utile?

Soluzione 3

Ho finito per imbattermi in un'applet di uploader Java open source e ho trovato tutto ciò che dovevo sapere nel suo codice. Ecco i collegamenti a un post sul blog che lo descrive così come la fonte:

Articolo
Codice sorgente

Altri suggerimenti

Per verificare l'avanzamento utilizzando HttpClient, avvolgere MultipartRequestEntity attorno a uno che conteggi i byte inviati. Il wrapper è sotto:

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.httpclient.methods.RequestEntity;

public class CountingMultipartRequestEntity implements RequestEntity {
    private final RequestEntity delegate;

    private final ProgressListener listener;

    public CountingMultipartRequestEntity(final RequestEntity entity,
            final ProgressListener listener) {
        super();
        this.delegate = entity;
        this.listener = listener;
    }

    public long getContentLength() {
        return this.delegate.getContentLength();
    }

    public String getContentType() {
        return this.delegate.getContentType();
    }

    public boolean isRepeatable() {
        return this.delegate.isRepeatable();
    }

    public void writeRequest(final OutputStream out) throws IOException {
        this.delegate.writeRequest(new CountingOutputStream(out, this.listener));
    }

    public static interface ProgressListener {
        void transferred(long num);
    }

    public static class CountingOutputStream extends FilterOutputStream {

        private final ProgressListener listener;

        private long transferred;

        public CountingOutputStream(final OutputStream out,
                final ProgressListener listener) {
            super(out);
            this.listener = listener;
            this.transferred = 0;
        }

        public void write(byte[] b, int off, int len) throws IOException {
            out.write(b, off, len);
            this.transferred += len;
            this.listener.transferred(this.transferred);
        }

        public void write(int b) throws IOException {
            out.write(b);
            this.transferred++;
            this.listener.transferred(this.transferred);
        }
    }
}

Quindi implementa un ProgressListener che aggiorna una barra di avanzamento.
Ricorda che l'aggiornamento della barra di avanzamento non deve essere eseguito nel thread di invio eventi.

Un countingEntity più semplice non dipenderebbe da un tipo di entità specifico, ma piuttosto estendere HttpEntityWrapped :

package gr.phaistos.android.util;

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.http.HttpEntity;
import org.apache.http.entity.HttpEntityWrapper;

public class CountingHttpEntity extends HttpEntityWrapper {

    public static interface ProgressListener {
        void transferred(long transferedBytes);
    }


    static class CountingOutputStream extends FilterOutputStream {

        private final ProgressListener listener;
        private long transferred;

        CountingOutputStream(final OutputStream out, final ProgressListener listener) {
            super(out);
            this.listener = listener;
            this.transferred = 0;
        }

        @Override
        public void write(final byte[] b, final int off, final int len) throws IOException {
            //// NO, double-counting, as super.write(byte[], int, int) delegates to write(int).
            //super.write(b, off, len);
            out.write(b, off, len);
            this.transferred += len;
            this.listener.transferred(this.transferred);
        }

        @Override
        public void write(final int b) throws IOException {
            out.write(b);
            this.transferred++;
            this.listener.transferred(this.transferred);
        }

    }


    private final ProgressListener listener;

    public CountingHttpEntity(final HttpEntity entity, final ProgressListener listener) {
        super(entity);
        this.listener = listener;
    }

    @Override
    public void writeTo(final OutputStream out) throws IOException {
        this.wrappedEntity.writeTo(out instanceof CountingOutputStream? out: new CountingOutputStream(out, this.listener));
    }
}

La quantità di byte restituiti dal listener è diversa dalla dimensione del file originale. Quindi, invece di avere trasferito ++ , l'ho modificato in modo che transfer = len ; questa è la lunghezza della quantità effettiva di byte che vengono scritti nel flusso di output. E quando computo l'aggiunta dei byte totali trasferiti è uguale all'effettivo ContentLength restituito da CountingMultiPartEntity.this.getContentLength();;

public void write(byte[] b, int off, int len) throws IOException {
    wrappedOutputStream_.write(b,off,len);
    transferred=len;
    listener_.transferred(transferred);
}

Potresti trovare utile questo articolo . Spiega in dettaglio usando HttpClient e FileUpload, entrambi i progetti apache per fare quello che vuoi. Include anche esempi di codice.

Tieni presente che la barra di avanzamento potrebbe essere fuorviante quando un componente intermedio nella rete (ad esempio un proxy HTTP dell'ISP o un proxy HTTP inverso davanti al server) consuma il tuo caricamento più velocemente di quanto non faccia il server.

Come notato dall'articolo pubblicato da Vincent, puoi usare i comuni di Apache per farlo.

Piccolo frammento


DiskFileUpload upload = new DiskFileUpload();
upload.setHeaderEncoding(ConsoleConstants.UTF8_ENCODING);

upload.setSizeMax(1000000);
upload.setSizeThreshold(1000000);

Iterator it = upload.parseRequest((HttpServletRequest) request).iterator();
FileItem item;
while(it.hasNext()){
    item = (FileItem) it.next();
    if (item.getFieldName("UPLOAD FIELD"){
       String fileName = item.getString(ConsoleConstants.UTF8_ENCODING);
       byte[] fileBytes = item.get();
    }
}

Solo il mio valore 2c:

Questo si basa sulla risposta di Tuler (ha un bug al momento della scrittura). L'ho modificato leggermente, quindi ecco la mia versione di tuler e mmyers answer (non riesco a modificare la loro risposta). Volevo provare a renderlo un po 'più pulito e veloce. Oltre al bug (che discuterò nei commenti sulla loro risposta), il grosso problema che ho con la loro versione è che crea un nuovo CountingOutputStream ad ogni scrittura. Questo può diventare molto costoso in termini di memoria: tonnellate di allocazioni e raccolte di rifiuti. Il problema minore è che utilizza un delegato quando potrebbe semplicemente espandere MultipartEntity . Non so perché l'hanno scelto, quindi l'ho fatto in un modo che mi era più familiare. Se qualcuno conosce i pro / contro dei due approcci sarebbe fantastico. Infine, il metodo FilterOutputStream # write (byte [], int, int) chiama semplicemente FilterOutputStream # write (byte) in un ciclo. La Documentazione FOS raccomanda di sostituire le sottoclassi questo comportamento e renderlo più efficiente. Il modo migliore per farlo qui è lasciare che OutputStream sottostante gestisca la richiesta di scrittura.

import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;

import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;

public class CountingMultiPartEntity extends MultipartEntity {

    private UploadProgressListener listener_;
    private CountingOutputStream outputStream_;
    private OutputStream lastOutputStream_;

    // the parameter is the same as the ProgressListener class in tuler's answer
    public CountingMultiPartEntity(UploadProgressListener listener) {
        super(HttpMultipartMode.BROWSER_COMPATIBLE);
        listener_ = listener;
    }

    @Override
    public void writeTo(OutputStream out) throws IOException {
        // If we have yet to create the CountingOutputStream, or the
        // OutputStream being passed in is different from the OutputStream used
        // to create the current CountingOutputStream
        if ((lastOutputStream_ == null) || (lastOutputStream_ != out)) {
            lastOutputStream_ = out;
            outputStream_ = new CountingOutputStream(out);
        }

        super.writeTo(outputStream_);
    }

    private class CountingOutputStream extends FilterOutputStream {

        private long transferred = 0;
            private OutputStream wrappedOutputStream_;

        public CountingOutputStream(final OutputStream out) {
            super(out);
                    wrappedOutputStream_ = out;
        }

        public void write(byte[] b, int off, int len) throws IOException {
                    wrappedOutputStream_.write(b,off,len);
                    ++transferred;
            listener_.transferred(transferred);
        }

        public void write(int b) throws IOException {
            super.write(b);
        }
    }
}

Cerca Client HTTP per caricare il file sul Web. Dovrebbe essere in grado di farlo. Non sono sicuro di come ottenere la barra di avanzamento, ma in qualche modo comporterebbe l'interrogazione dell'API.

Apache common è un'ottima opzione. Apache common ti consente di configurare le seguenti cose.

  1. Configura (file xml) la dimensione massima del file / carica la dimensione del file
  2. Percorso di destinazione (dove salvare il file caricato)
  3. Imposta la temp. cartella per scambiare il file, in modo che il caricamento del file sia veloce.

Dalle altre risposte puoi semplicemente ignorare il metodo AbstractHttpEntity o le implementazioni della classe public void writeTo (OutputStream outstream) che stai utilizzando se non vuoi creare una classe .

Un esempio che utilizza un'istanza FileEntity :

FileEntity fileEntity = new FileEntity(new File("img.jpg")){
    @Override
    public void writeTo(OutputStream outstream) throws IOException {
        super.writeTo(new BufferedOutputStream(outstream){
            int writedBytes = 0;

            @Override
            public synchronized void write(byte[] b, int off, int len) throws IOException {
                super.write(b, off, len);

                writedBytes+=len;
                System.out.println("wrote: "+writedBytes+"/"+getContentLength()); //Or anything you want [using other threads]
            }
        });
    }

};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top