A ZIP fájlok végső méretének becslése nehéz feladat, de a végső fájlméretet viszonylag pontosan is meg lehet határozni olyan módon, hogy mintákat veszünk a fájlból és a mintákon elvégezzük a tömörítést, majd a tömörített minták méretéből megbecsüljük a teljes tömörített állomány méretét.
Az általam fejlesztett megvalósításban:
- ha kisebb a fájl a BUFFER_SIZE mintaméretnél, akkor az egészt fájlt betömörítem a memóriában, így a pontos méretet kapom
- ha nagyobb a fájlméret a mintánál, akkor ZIP_SAMPLES mennyiségű mintát veszek a fájlból, a mintákat összefűzöm egy egységes tömbbé, a minta tömböt tömörítem össze a memóriában, végül a tömörített tömb méretéből kiszámolom a teljes állomány becsült méretét.
Az így kapott becsült tömörített fájlméret jól közelíti a valódi méretet.
A teljes osztályt innen lehet letölteni:
A következő metódus akár egy több GByte-os fájl méretét is hatékonyan és gyorsan meghatározza:
public static long sizeOfZippedFile(File file) throws FileNotFoundException, IOException {
long fullLength = Math.min(Integer.MAX_VALUE, file.length());
int parts = (int) (fullLength / BUFFER_SIZE);
if (parts <= ZIP_SAMPLES) {
return exactSizeOfZippedFile(file);
}
byte[] sampleArray = new byte[ZIP_SAMPLES * BUFFER_SIZE];
long ratio = parts / ZIP_SAMPLES;
int pos = 0;
int fullPos = 0;
try (FileInputStream fis = new FileInputStream(file)) {
while (fullPos < parts) {
if (fullPos % ratio == 0 && pos < ZIP_SAMPLES) {
fis.read(sampleArray, pos * BUFFER_SIZE, BUFFER_SIZE);
pos += 1;
} else {
fis.skip(BUFFER_SIZE);
}
fullPos += 1;
}
}
return (long) (exactSizeOfZippedByteArray(sampleArray) * (((double) file.length()) / sampleArray.length));
}
Ha a vizsgált fájl mérete elég kicsi, a következő metódusal számoljuk ki a pontos tömörített méretet:
public static long exactSizeOfZippedFile(File file) throws FileNotFoundException, IOException {
try (LenghtOutputStream sos = new LenghtOutputStream();) {
try (ZipOutputStream zos = new ZipOutputStream(sos);) {
int bytesIn = 0;
byte[] buffer = new byte[1024];
try (FileInputStream fis = new FileInputStream(file)) {
ZipEntry anEntry = new ZipEntry(file.getName());
zos.putNextEntry(anEntry);
while ((bytesIn = fis.read(buffer)) != -1) {
zos.write(buffer, 0, bytesIn);
}
}
}
return sos.getLength();
}
}
A minta tömb tömörített méretét a következő metódussal lehet meghatározni:
public static long exactSizeOfZippedByteArray(byte[] file)
throws IOException {
Deflater oDeflate = new Deflater();
oDeflate.setInput(file);
oDeflate.finish();
try (LenghtOutputStream los = new LenghtOutputStream();) {
byte[] byRead = new byte[BUFFER_SIZE];
while (!oDeflate.finished()) {
int iBytesRead = oDeflate.deflate(byRead);
if (iBytesRead == byRead.length) {
los.write(byRead);
} else {
los.write(byRead, 0, iBytesRead);
}
}
oDeflate.end();
return los.getLength();
}
}
A streamek pontos ZIP-elt méret meghatározásához nem kell mást tenni, mint készíteni egy dummy OutputStream-et, amely nem tesz mást, csak megszámolja a neki átadott bájtokat:
public class LengthOutputStream extends OutputStream {
private long length = 0L;
@Override
public void write(int b) throws IOException {
length++;
}
public long getLength() {
return length;
}
}
LengthOutputStream-et egyszerűen átadjuk a ZipOutputStream objektumnak, így a tömörített adatfolyamot a ZipOutputStream a LengthOutputStream-nek adja át, ami megszámolja a bájtokat, és a végén le tudjuk kérdezni tőle a ZIP méretét:
public static long sizeOfZippedDirectory(File dir) throws FileNotFoundException, IOException {
try (LengthOutputStream sos = new LengthOutputStream();
ZipOutputStream zos = new ZipOutputStream(sos);) {
... // Fájlok hozzáadás a ZIP folyamhoz
return sos.getLength();
}
}