- Megjelent: 2017. május 09
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(); } }