2009-05-05 3 views
4

Aimez ce site! Mon problème est le suivant:ZipInputStream ne signale pas * réel * (c'est-à-dire compressé) octets lu

Je lis un fichier zip qui arrive sur un réseau à partir d'une requête HTTP "PUT". L'en-tête de demande me dit que la longueur de contenu est (disons) 1Mb. Le code suivant crée le ZipInputStream, et enregistre le contenu zip aux fichiers dans le répertoire courant:

ZipInputStream zis = new ZipInputStream(inputStream); 
ZipEntry ze; 
long totalBytesRead = 0; 
while ((ze = zis.getNextEntry()) != null) { 
    BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(ze.getName())); 
    byte[] buffer = new byte[4096]; 
    int i; 
    while ((i = zis.read(buffer)) != -1) { 
     totalBytesRead+=i; 
     outStream.write(buffer,0,i); 
    } 
    outStream.close(); 
} 
inputStream.close(); 

Quand tout est dit et fait, totalBytesRead est égale à environ 1,5 Mo (en fonction de la compression des fichiers, pourrait être quoi que ce soit bien!). Ce que je voudrais savoir est s'il y a un moyen de savoir combien d'octets réels ont été lus à partir de l'original inputStream? Les deux ze.getSize() et ze.getCompressedSize() renvoient -1 pour chaque entrée compressée (c'est-à-dire qu'elle ne sait pas). J'ai besoin de cette information pour une barre de progression pour montrer combien d'octets du fichier zip transmis ont été lus sur le réseau.

Suggestions? Devrais-je peut-être sous-classer ZipInputStream et essayer de savoir combien d'octets il lit à partir de son InputStream enveloppé?

Merci d'avance!

Répondre

1

Bien sûr, cela semble raisonnable.

Il existe essentiellement deux options: lire tous les octets, les stocker (en mémoire ou dans un fichier), les compter, puis les décompresser; ou les compter comme ils viennent. Le premier semble inefficace, et le dernier nécessitera une sous-classe de InputStream qui a la capacité de compter les octets qu'il lit. Je ne peux pas penser à un dans la bibliothèque standard, mais les implémentations existent probablement là - là encore, il serait assez facile d'écrire les vôtres.

2
import java.io.FilterInputStream; 
import java.io.IOException; 
import java.io.InputStream; 

/** 
* 
*/ 

/** 
* @author clint 
* 
*/ 
public class ByteCountingInputStream extends FilterInputStream { 

    public int totalRead = 0; 

    /** 
    * @param in 
    */ 
    protected ByteCountingInputStream(InputStream in) { 
    super(in); 
    // TODO Auto-generated constructor stub 
    } 

    /* (non-Javadoc) 
    * @see java.io.FilterInputStream#read() 
    */ 
    @Override 
    public int read() throws IOException { 
    int ret = super.read(); 
    totalRead++; 
    return ret; 
    } 

    /* (non-Javadoc) 
    * @see java.io.FilterInputStream#read(byte[], int, int) 
    */ 
    @Override 
    public int read(byte[] b, int off, int len) throws IOException { 
    int ret = super.read(b, off, len); 
    totalRead += ret; 
    return ret; 
    } 

    /* (non-Javadoc) 
    * @see java.io.FilterInputStream#read(byte[]) 
    */ 
    @Override 
    public int read(byte[] b) throws IOException { 
    int ret = super.read(b); 
    totalRead += ret; 
    return ret; 
    } 

    /* (non-Javadoc) 
    * @see java.io.FilterInputStream#skip(long) 
    */ 
    @Override 
    public long skip(long n) throws IOException { 
    //What to do? 
    return super.skip(n); 
    } 

    /** 
    * @return the totalRead 
    */ 
    protected int getTotalRead() { 
    return this.totalRead; 
    } 

} 

Cela va entre comme

ZipInputStream zis = new ZipInputStream(new ByteCountingInputStream(inputStream)); 
+0

pourquoi le constructeur 'protected'? –

2

Merci à vous deux! Je viens de finir de faire à peu près exactement ce que Clint a suggéré!

import java.io.FilterInputStream; 
import java.io.IOException; 
import java.io.InputStream; 

public class CountingInputStream extends FilterInputStream { 

    private long totalBytes = 0; 

    protected CountingInputStream(InputStream in) { 
     super(in); 
    } 

    public int getTotalBytesRead() { 
     return totalBytes; 
    } 

    @Override 
    public int read() throws IOException { 
     int byteValue = super.read(); 
     if (byteValue != -1) totalBytes++; 
     return byteValue; 
    } 

    @Override 
    public int read(byte[] b) throws IOException { 
     int bytesRead = super.read(b); 
     if (bytesRead != -1) totalBytes+=bytesRead; 
     return bytesRead; 
    } 

    @Override 
    public int read(byte[] b, int off, int len) throws IOException { 
     int bytesRead = super.read(b,off,len); 
     if (bytesRead != -1) totalBytes+=bytesRead; 
     return bytesRead; 
    } 
} 

Maintenant je me demande qui je devrais donner la petite "coche" à ...?

Merci encore!

+0

Oui, la vérification -1 est une bonne adaptation. – Clint

-1

C'est ce que je fais ... pas besoin de surcharger quoi que ce soit.

ZipInputStream zis = new ZipInputStream(inputStream); 
ZipEntry ze; 
int totalBytes = inputStream.available(); 
int totalBytesRead = 0; 
while ((ze = zis.getNextEntry()) != null) { 
    totalBytesRead = totalBytes - inputStream.available(); 
    BufferedOutputStream outStream = new BufferedOutputStream(new FileOutputStream(ze.getName())); 
    byte[] buffer = new byte[4096]; 
    int i; 
    while ((i = zis.read(buffer)) != -1) { 
     outStream.write(buffer,0,i); 
    } 
    outStream.close(); 
} 
inputStream.close(); 
+0

http://docs.oracle.com/javase/7/docs/api/java/util/zip/ZipInputStream.html#available() Retourne 0 après que EOF a atteint les données d'entrée en cours, sinon renvoie toujours 1. Les programmes ne doivent pas compter sur cette méthode pour renvoyer le nombre réel d'octets pouvant être lus sans blocage. –

Questions connexes