Question

Alors, je l'alimentation des données de fichier à une API qui prend un Reader, et je voudrais un moyen de signaler des progrès.

Il semble que ce devrait être facile d'écrire une implémentation FilterInputStream qui enveloppe la FileInputStream, garde la trace du nombre d'octets lus par rapport à la taille totale du fichier, et déclenche un événement (ou, appelle une méthode de update()) de faire rapport fractionnel progrès.

(Sinon, il pourrait déclarer octets absolus lire, et quelqu'un d'autre pourrait faire le calcul -. Peut-être plus généralement utile dans le cas d'autres situations de diffusion en continu)

Je sais que je l'ai vu avant et je l'ai fait même, mais je ne peux pas trouver le code et je suis paresseux. Quelqu'un at-il autour de la pose? Ou quelqu'un peut-il suggérer une meilleure approche?


Un an (et un peu) plus tard ...

Je mis en œuvre une solution basée sur la réponse de Adamski ci-dessous, et cela a fonctionné, mais je ne recommanderais pas après quelques mois d'utilisation, il. Lorsque vous avez beaucoup de mises à jour, mise à feu / gestion des événements de progrès inutiles devient un coût énorme. Le mécanisme de comptage de base est bien, mais beaucoup mieux d'avoir celui qui se soucie du sondage progrès pour elle, plutôt que de pousser à eux.

(Si vous connaissez la taille totale, vous pouvez essayer seulement tirer un événement tous> 1% de changement ou autre chose, mais pas vraiment la peine. Et souvent, vous ne sont pas.)

Était-ce utile?

La solution

Voici une application assez basique qui se déclenche PropertyChangeEvents sont lus octets quand supplémentaires. Quelques mises en garde:

  • La classe ne prend pas en charge les opérations de mark ou reset, bien que ceux-ci seraient faciles à ajouter.
  • La classe ne vérifie pas si le nombre total d'octets jamais lu dépasse le nombre maximum d'octets attendus, bien que cela puisse toujours être traitée par le code client lors de l'affichage des progrès.
  • Je n'ai pas tester le code.

Code:

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

Autres conseils

Goyave package de l 'com.google.common.io peut vous aider un peu. Ce qui suit est décompilé et non testé, mais devrait vous mettre sur la bonne voie.

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

Cela peut ressembler à beaucoup de code, mais je crois qu'il est le moins vous sortir avec. (Note d'autres réponses à cette question ne donnent pas complet exemples de code, il est donc difficile de les comparer de cette façon.)

La réponse par Adamski fonctionne, mais il y a un petit bug. La méthode de read(byte[] b) surchargée appelle la méthode read(byte[] b, int off, int len) cuvette super classe.
Alors updateProgress(long numBytesRead) est appelé deux fois pour chaque lecture action et vous vous retrouvez avec un numBytesRead qui est deux fois la taille du fichier après le fichier total a été lu.

Non méthode redéfinie de read(byte[] b) résout le problème.

Si vous construisez une application graphique, il y a toujours ProgressMonitorInputStream . S'il n'y a pas d'interface graphique impliqué envelopper un InputStream de la façon que vous décrivez est une évidence et prend moins de temps que de poster une question ici.

Pour compléter la réponse donnée par Bourillion @ Kevin, il peut être appliqué à une teneur en réseau et en utilisant cette technique (qui empêche la lecture du flux deux fois: l'une pour la taille et l'autre pour le contenu):

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

Où ProgressByteProcessor est une classe interne:

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;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top