Domanda

Quindi, sto fornendo i dati dei file a un'API che accetta un file Reader, e vorrei un modo per segnalare i progressi.

Sembra che dovrebbe essere semplice scrivere a FilterInputStream implementazione che avvolge il FileInputStream, tiene traccia del numero di byte letti rispetto a quelli letti.la dimensione totale del file e attiva alcuni eventi (o chiama alcuni update() metodo) per segnalare progressi frazionari.

(In alternativa, potrebbe riportare i byte assoluti letti e qualcun altro potrebbe fare i conti, forse più generalmente utile nel caso di altre situazioni di streaming.)

So di averlo già visto e potrei anche averlo fatto prima, ma non riesco a trovare il codice e sono pigro.Qualcuno lo ha in giro?Oppure qualcuno può suggerire un approccio migliore?


Un anno (e poco) dopo...

Ho implementato una soluzione basata sulla risposta di Adamski di seguito e ha funzionato, ma dopo alcuni mesi di utilizzo non la consiglierei.Quando hai molti aggiornamenti, attivare/gestire eventi di progresso non necessari diventa un costo enorme.Il meccanismo di conteggio di base va bene, ma è molto meglio avere chiunque abbia a cuore il sondaggio sui progressi, piuttosto che imporglielo.

(Se conosci la dimensione totale, puoi provare ad attivare un evento solo ogni modifica > 1% o qualsiasi altra cosa, ma non ne vale davvero la pena.E spesso non lo fai.)

È stato utile?

Soluzione

Ecco un'implementazione abbastanza di base che spara PropertyChangeEvents quando i byte vengono letti aggiuntivi. Alcuni avvertimenti:

  • La classe non supporta operazioni mark o reset, anche se questi sarebbero facili da aggiungere.
  • La classe non controlla se il numero totale byte letti mai supera il numero massimo di byte attesi, anche se questo potrebbe sempre essere affrontato dal codice client per la visualizzazione di avanzamento.
  • Non ho testare il codice.

Codice:

public class ProgressInputStream extends FilterInputStream {
    private final PropertyChangeSupport propertyChangeSupport;
    private final long maxNumBytes;
    private volatile long totalNumBytesRead;

    public ProgressInputStream(InputStream in, long maxNumBytes) {
        super(in);
        this.propertyChangeSupport = new PropertyChangeSupport(this);
        this.maxNumBytes = maxNumBytes;
    }

    public long getMaxNumBytes() {
        return maxNumBytes;
    }

    public long getTotalNumBytesRead() {
        return totalNumBytesRead;
    }

    public void addPropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.addPropertyChangeListener(l);
    }

    public void removePropertyChangeListener(PropertyChangeListener l) {
        propertyChangeSupport.removePropertyChangeListener(l);
    }

    @Override
    public int read() throws IOException {
        int b = super.read();
        updateProgress(1);
        return b;
    }

    @Override
    public int read(byte[] b) throws IOException {
        return (int)updateProgress(super.read(b));
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return (int)updateProgress(super.read(b, off, len));
    }

    @Override
    public long skip(long n) throws IOException {
        return updateProgress(super.skip(n));
    }

    @Override
    public void mark(int readlimit) {
        throw new UnsupportedOperationException();
    }

    @Override
    public void reset() throws IOException {
        throw new UnsupportedOperationException();
    }

    @Override
    public boolean markSupported() {
        return false;
    }

    private long updateProgress(long numBytesRead) {
        if (numBytesRead > 0) {
            long oldTotalNumBytesRead = this.totalNumBytesRead;
            this.totalNumBytesRead += numBytesRead;
            propertyChangeSupport.firePropertyChange("totalNumBytesRead", oldTotalNumBytesRead, this.totalNumBytesRead);
        }

        return numBytesRead;
    }
}

Altri suggerimenti

Guaiava'S com.google.common.io il pacchetto può aiutarti un po'.Quanto segue non è compilato e non testato ma dovrebbe metterti sulla strada giusta.

long total = file1.length();
long progress = 0;
final OutputStream out = new FileOutputStream(file2);
boolean success = false;
try {
  ByteStreams.readBytes(Files.newInputStreamSupplier(file1),
      new ByteProcessor<Void>() {
        public boolean processBytes(byte[] buffer, int offset, int length)
            throws IOException {
          out.write(buffer, offset, length);
          progress += length;
          updateProgressBar((double) progress / total);
          // or only update it periodically, if you prefer
        }
        public Void getResult() {
          return null;
        }
      });
  success = true;
} finally {
  Closeables.close(out, !success);
}

Potrebbe sembrare un sacco di codice, ma credo che sia il minimo con cui riuscirai a farla franca.(nota che altre risposte a questa domanda non vengono fornite completare esempi di codice, quindi è difficile confrontarli in questo modo.)

La risposta da Adamski funziona, ma c'è un piccolo bug. Il metodo read(byte[] b) override chiama il metodo read(byte[] b, int off, int len) trogolo classe eccellente.
Così updateProgress(long numBytesRead) viene chiamato due volte per ogni lettura azione e si finisce con un numBytesRead che è il doppio della dimensione del file dopo il file totale è stato letto.

Non prioritario metodo read(byte[] b) risolve il problema.

Se si sta costruendo un'applicazione GUI c'è sempre ProgressMonitorInputStream . Se non c'è GUI coinvolti avvolgendo un InputStream nel modo in cui si descrive è un gioco da ragazzi e richiede meno tempo di quanto la pubblicazione di una domanda qui.

Per completare la risposta data da @ Kevin Bourillion, può essere applicato ad un contenuto di rete così usando questa tecnica (che impedisce lettura del flusso di due volte: una per dimensione e una per il contenuto):

        final HttpURLConnection httpURLConnection = (HttpURLConnection) new URL( url ).openConnection();
        InputSupplier< InputStream > supplier = new InputSupplier< InputStream >() {

            public InputStream getInput() throws IOException {
                return httpURLConnection.getInputStream();
            }
        };
        long total = httpURLConnection.getContentLength();
        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ByteStreams.readBytes( supplier, new ProgressByteProcessor( bos, total ) );

Dove ProgressByteProcessor è una classe interna:

public class ProgressByteProcessor implements ByteProcessor< Void > {

    private OutputStream bos;
    private long progress;
    private long total;

    public ProgressByteProcessor( OutputStream bos, long total ) {
        this.bos = bos;
        this.total = total;
    }

    public boolean processBytes( byte[] buffer, int offset, int length ) throws IOException {
        bos.write( buffer, offset, length );
        progress += length - offset;
        publishProgress( (float) progress / total );
        return true;
    }

    public Void getResult() {
        return null;
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top