Я использую следующий автономный класс для расчета размера заархивированных файлов перед архивированием. Я использую сжатие с нулевым уровнем, но все же разница в несколько байтов. Не могли бы вы помочь мне узнать точный размер?

Быстрая помощь будет оценена.

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.FilenameUtils;


public class zipcode {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub



         try {
             CRC32 crc = new CRC32();

                byte[] b = new byte[1024]; 
                File file = new File("/Users/Lab/Desktop/ABC.xlsx");
            FileInputStream in = new FileInputStream(file);
            crc.reset();
                // out put file 
                ZipOutputStream out = new ZipOutputStream(new FileOutputStream("/Users/Lab/Desktop/ABC.zip"));


                // name the file inside the zip  file 

                ZipEntry entry = new ZipEntry("ABC.xlsx");
                entry.setMethod(ZipEntry.DEFLATED);
                entry.setCompressedSize(file.length());
                entry.setSize(file.length());
                entry.setCrc(crc.getValue());
                out.setMethod(ZipOutputStream.DEFLATED);
                out.setLevel(0);
                //entry.setCompressedSize(in.available());
                //entry.setSize(in.available());
                //entry.setCrc(crc.getValue());


                out.putNextEntry(entry); 
                // buffer size

                int count;

                while ((count = in.read(b)) > 0) {
                    System.out.println();
                    out.write(b, 0, count);
                }
                out.close();
                in.close();         
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }

}
0
user3547705 18 Апр 2014 в 09:18

2 ответа

Лучший ответ

Во-первых, меня не убеждают объяснения, зачем вам это нужно. Что-то не так с дизайном или реализацией вашей системы, если необходимо знать размер файла до начала загрузки.

Сказав это, решение состоит в том, чтобы создать ZIP-файл на стороне сервера, чтобы вы знали его размер до того, как начнете загружать его клиенту:

  • Запишите ZIP-файл во временный файл и загрузите из него.

  • Запишите ZIP-файл в буфер в памяти и загрузите из него.

Если у вас нет ни файлового пространства, ни памяти на стороне сервера, тогда:

  • Создайте «приемник» outputStream, который просто подсчитывает записанные байты для расчета номинального размера файла.

  • Создайте / запишите ZIP-файл в приемник и зафиксируйте размер файла.

  • Откройте ваше соединение для загрузки.

  • Отправьте метаданные, включая размер файла.

  • Создайте / запишите ZIP во второй раз, записав в поток сокета ... или что-то еще.

Эти 3 подхода позволят вам создать и отправить сжатый ZIP-файл, если это поможет.


Если вы настаиваете на том, чтобы попытаться сделать это на лету за один проход, вам нужно будет прочитать спецификацию ZIP-файла в криминалистических деталях ... и выполнить некоторую беспорядочную арифметику. Помощь вам, вероятно, выходит за рамки вопроса SO.

2
Stephen C 4 Май 2020 в 08:18

Мне пришлось сделать это самому, чтобы записать результаты zip прямо в AWS S3, для чего требуется размер файла. К сожалению, я не нашел способа вычислить размер сжатого файла без выполнения вычислений для каждого блока данных.

Один из способов - это заархивировать все дважды. В первый раз, когда вы выбрасываете данные, но складываете количество байтов:

    long getSize(List<InputStream> files) throws IOException {
        final AtomicLong counter = new AtomicLong(0L);
        final OutputStream countingStream = new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                counter.incrementAndGet();
            }
        };
        ZipOutputStream zoutcounter = new ZipOutputStream(countingStream);
        // Loop through files or input streams here and do compression
        // ...
        zoutcounter.close();
            
        return counter.get();
    }

Альтернативой является выполнение описанного выше, создавая запись для каждого файла, но не записывая никаких фактических данных (не вызывайте write ()), чтобы вы могли вычислить общий размер только заголовков записей zip. Это будет работать, только если вы отключите сжатие следующим образом:

entry.setMethod(ZipEntry.STORED);

Размер записей zip плюс размер каждого несжатого файла должен дать вам точный окончательный размер, но только с выключенным сжатием. Вам не нужно устанавливать значения CRC или какие-либо другие поля при вычислении размера zip-файла, поскольку эти записи всегда имеют одинаковый размер в окончательном заголовке записи. Только поля name, comment и extra в ZipEntry различаются по размеру. Другие записи, такие как размер файла, CRC и т. Д., Занимают одинаковое место в конечном zip-файле независимо от того, были они установлены или нет.

Вы можете попробовать еще одно решение. Угадайте размер консервативно и добавьте запас прочности, а затем сожмите его агрессивно. Разместите остальную часть файла, пока она не станет равной вашему предполагаемому размеру. Zip игнорирует заполнение. Если вы реализуете выходной поток, который обертывает ваш фактический выходной поток, но реализует операцию закрытия как noop, вы можете передать это как выходной поток для вашего ZipOutputStream. После того, как вы закроете свой экземпляр ZipOutputStream, напишите заполнение в фактический выходной поток, чтобы оно равнялось вашему расчетному количеству байтов, а затем закройте его по-настоящему. Файл будет больше, чем мог бы быть, но вы сохраните вычисление точного размера файла, и результат выиграет от хотя бы некоторого сжатия.

0
Theron 15 Янв 2021 в 02:33