Можно ли объединить ZipOutputStream и DigestOutputstream?

#java #stream #zip #checksum

#java #поток #zip #контрольная сумма

Вопрос:

Мне нужно определить контрольную сумму .zip файла перед его загрузкой куда-либо, чтобы обеспечить целостность файла.

В настоящее время у меня есть что-то вроде следующего:

         for (File file : zipCandidates) {
            InputStream fileInputStream = new BufferedInputStream(new FileInputStream(file));
            ZipUtils.addDataToZip(zipStream, fileInputStream, file.getName());
            boolean deleted = file.delete();
            if (!deleted) {
                log.error("Failed to delete temporary file {} : {}", file.getName(), file.getAbsolutePath());
            }
        }
        zipStream.close();

        // checksum and filesize
        long fileSize = zipFile.length();
        InputStream fileInputStream = FileUtils.openInputStream(zipFile);
        BufferedInputStream bufferedFileInputStream = new BufferedInputStream(fileInputStream);
        String checksum = DigestUtils.md5Hex(bufferedFileInputStream);

        bufferedFileInputStream.close();


        // upload
        fileInputStream = FileUtils.openInputStream(zipFile);
        bufferedFileInputStream = new BufferedInputStream(fileInputStream);
        val writer = writerFactory.createWriter(blobName, fileSize, checksum);
        writer.write(bufferedFileInputStream);

        bufferedFileInputStream.close();
  

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

Есть ли какой-нибудь способ объединить мои ZipOutputStream above и a DigestOutputstream , чтобы я мог просто обновить свою контрольную сумму при записи zip-файла? К сожалению, поскольку выходной поток должен быть a ZipOutputStream , я не могу просто украсить его (т.Е. new DigestOutputStream(zipStream, digest) ).

Ответ №1:

К сожалению, поскольку выходной поток должен быть a ZipOutputStream , я не могу просто украсить его (т.Е. new DigestOutputStream(zipStream, digest) ).

Вы бы все равно этого не хотели, потому что вы хотите переварить результат операции архивирования, поэтому вам нужно обернуть DigestOutputStream с ZipOutputStream помощью, то есть другим способом:

 try (ZipOutputStream zipStream = new ZipOutputStream(
                                   new DigestOutputStream(
                                     new FileOutputStream(zipFile),
                                     digest))) {
    // code adding zip entries here
}
String checksum = Hex.encodeHexString(digest.digest());
  

Обратите внимание на использование try-with-resources, чтобы убедиться, что ваш ZipOutputStream файл всегда закрыт правильно.

Комментарии:

1. Я понимаю. Я предполагаю, что я неправильно истолковал, как DigestOutputStream функционирует — я не понимал, что поток digest будет изменен потоком и может быть прочитан позже. Спасибо.

Ответ №2:

Вы, конечно, можете создать собственный выходной поток, который обертывает два выходных потока (в вашем конкретном случае один будет вашим ZipOutputStream, а другой — вашим DigestOutputStream). Ваша новая реализация потока вывода будет записывать каждый байт, который он получает, в оба обернутых потока.

Этот вариант использования достаточно распространен, поэтому вы, вероятно, найдете версию с открытым исходным кодом, которая будет соответствовать вашим потребностям (например, эта из apache commons).