12
votes

Entrée ou wrapper de lecteur pour le rapport d'avancement

Alors, je l'alimentation des données de fichier à une API qui prend un Lecteur , et je voudrais un moyen de progression du rapport.

On dirait qu'il devrait être simple d'écrire un filtreInputtream implémentation qui envoie le fichier fileInputStream , garde la trace du nombre d'octets lus par rapport à la taille totale du fichier, et déclenche un événement (ou, en appelle mise à jour () ) faire état de progrès fractionnelle.

(Alternativement, cela pourrait signaler des octets absolus, et quelqu'un d'autre pourrait faire les mathématiques - peut-être plus généralement utile dans le cas d'autres situations de streaming.)

Je sais que j'ai déjà vu cela avant et je peux même l'avoir fait auparavant, mais je ne trouve pas 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 après quelques mois d'utilisation, je ne le recommanderais pas. Lorsque vous avez beaucoup de mises à jour, tirez / gérer des événements de progrès inutiles devient un coût considérable. 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 uniquement de tirer un événement tous les> 1% de changement ou autre, mais cela ne vaut pas vraiment la peine. Et souvent, vous ne le faites pas.)


0 commentaires

5 Réponses :


1
votes

Si vous construisez une application d'interface graphique, il y a toujours ProgressMonitorInputStream . S'il n'y a pas d'interface graphique d'interface graphique d'entrer un introuvable dans la manière dont vous décrivez est un point évident et prend moins de temps que de poster une question ici.


1 commentaires

Ouais, j'ai regardé ça. C'est une application d'interface graphique, mais elle est assez complexe et que les rapports d'avancement ne sont pas une question de faire apparaître une standard progressMonitor . Emballage du InputStream prendrait moins de temps que de poster une question, mais je ne saurais jamais si quelqu'un est une meilleure idée.



14
votes

Voici une implémentation assez basique qui incendie PropertyChangeEvent S lorsque des octets supplémentaires sont lus. Quelques réserves:

  • La classe ne prend pas en charge Mark ou réinitialiser les opérations , bien que ce soit facile à ajouter.
  • La classe ne vérifie pas si le nombre total d'octets de lecture dépasse jamais le nombre maximal d'octets prévu, bien que cela puisse toujours être traité par le code client lors de l'affichage des progrès.
  • Je n'ai pas testé le code.

    code: xxx


4 commentaires

Pas mal. J'aurais probablement oublié sauter () . :)


@David: Pour être honnête Mise en œuvre Skip signifiait introduire des lanceurs de Nasty (int) Donc, si vous savez que vous n'en avez pas besoin, je jetterais une idée non supportée ici aussi.


Vous devez également vérifier que super.read () ne renvoie pas de négatif (fin des données).


Je pense qu'il y a toujours un bug là-bas. La mise en œuvre de Lecture (octet [] b) devrait simplement être Retour Lecture (B, 0, B.Length); ( Sans Progrès mettre à jour). Dans la mise en œuvre actuelle, appeler Lecture (octet [] B) fait compter les octets de lecture deux fois , parce que la mise en œuvre de filtreInputStream # lire (octet [] b) appelle directement Lecture (octet [] B, int Off, int Len) .



6
votes

GUAVA 'S com.google.common.io code> package peut aider vous un peu. Ce qui suit est intronuré 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);
}


2 commentaires

Connaissez-vous de manière fiable de le faire avec une URL et non un fichier? Quel bug me est le moyen de trouver la longueur du contenu de l'URL. Comme indiqué ici, il semble y avoir aucun moyen de connaître la taille du flux si nous utilisons la compression GZIP. Android-Developers.Blogspot.fr/2011/09/... < / a>


(Ce commentaire ne devrait-il pas être attaché à cette réponse alors?)



5
votes

La réponse par Adamski fonctionne, mais il y a un petit bug. Le remplacement Lecture (octet [] B) procédés appelle la méthode Lecture (octet [] B, int Off Off, int Off Off, int Len) Méthode The Super Class.
Donc, updateprogress (NumbytesReadeDeadeadeDeDead) est appelé deux fois pour chaque action de lecture et vous vous retrouvez avec un numbytesread c'est-à-dire deux fois la taille du fichier après que le fichier total a été lu.

ne pas remplacer lis (octet [] b) La méthode résout le problème.


1 commentaires

Cela doit être un commentaire de la réponse @adamski et non une réponse par elle-même au moins si vous ne mettez pas le code corrigé dans votre réponse.



0
votes

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

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


1 commentaires

PS: Cela ne doit pas fonctionner avec des urlconnections comprimées gzippées.