Поместите Inputstream, содержащий несколько файлов, в один ZipEntry - PullRequest
1 голос
/ 10 апреля 2019

Я хочу заархивировать массив File в zip-файл и отправить его в браузер.Inputstream каждого File является шейп-файлом и фактически состоит из нескольких файлов (.shp, .dbf, .shx, ...).

Когда я отправляю только один File сследующий код, он работает правильно, и возвращается zip-файл со всеми необходимыми файлами в нем.

Код для отправки одного файла

FileInputStream is = new FileInputStream(files.get(0));

response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + getCurrentUser(request).getNiscode() + ".zip");

while (is.available() > 0) {
    response.getOutputStream().write(is.read());
}

is.close();
if (response.getOutputStream() != null) {
    response.getOutputStream().flush();
    response.getOutputStream().close();
}

Когда я пытаюсьЧтобы отправить все файлы вместе, возвращается zip-файл с нужными папками, но в каждой папке присутствует только один элемент с расширением .file.Это как-то связано с записями ZipOutputStream?

кода для отправки всех файлов

byte[] zip = this.zipFiles(files, Ids);

response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename="test.zip");

response.getOutputStream().write(zip);
response.flushBuffer();
private byte[] zipFiles(ArrayList<File> files, String[] Ids) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream zos = new ZipOutputStream(baos);

        int count = 0;
        for (File file : files) {
            FileInputStream fis = new FileInputStream(file);

            zos.putNextEntry(new ZipEntry(Ids[count] + "/"));
            zos.putNextEntry(new ZipEntry(Ids[count] + "/" + file.getName()));

            while (fis.available() > 0) {
                zos.write(fis.read());
            }
            zos.closeEntry();
            fis.close();
            count ++;
        }
        zos.flush();
        baos.flush();
        zos.close();
        baos.close();

        return baos.toByteArray();
    }

Ответы [ 3 ]

2 голосов
/ 10 апреля 2019

Судя по вашему коду, каждый файл в вашем массиве files уже является zip-файлом

Когда вы позже выполните zipFiles, вы создаете zip-файл, который содержит больше zip-файлов в своих папках. Вы, очевидно, этого не хотите, но вам нужен zip-файл, в котором есть папки, содержащие содержимое всех возможных zip-файлов.

Основываясь на существующем существующем ответе Thanador, расположенном по адресу " Чтение данных из нескольких zip-файлов и объединение их в один ", я разработал следующее решение, включающее также каталоги и правильную обработку потока:

/**
 * Combine multiple zipfiles together
 * @param files List of file objects pointing to zipfiles
 * @param ids List of file names to use in the final output
 * @return The byte[] object representing the final output
 * @throws IOException When there was a problem reading a zipfile
 * @throws NullPointerException When there either input is or contains a null value
 */
private byte[] zipFiles(ArrayList<File> files, String[] ids) throws IOException {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    // Buffer to copy multiple bytes at once, this is generally faster that just reading and writing 1 byte at a time like your previous code does
    byte[] buf = new byte[16 * 1024];
    int length = files.size();
    assert length == ids.length;
    try (ZipOutputStream zos = new ZipOutputStream(baos)) {
        for (int i = 0; i < length; i++) {
            try (ZipInputStream inStream = new ZipInputStream(new FileInputStream(files.get(i))) {
                ZipEntry entry;
                while ((entry = inStream.getNextEntry()) != null) {
                    zos.putNextEntry(new ZipEntry(ids[i] + "/" + entry.getName()));
                    int readLength;
                    while ((readLength = inStream.read(buf)) > 0) {
                        zos.write(buf, 0, readLength);
                    }
                }
            }
        }
    }

    return baos.toByteArray();
}
  • Технически, это более быстрая и более эффективная память для прямой записи в выходной поток, полученный из response.getOutputStream(), но я не делал этого в приведенном выше примере, так что вам было бы проще реализовать метод в своем коде
  • Если вы закрываете поток, он автоматически сбрасывает его, я использую try-with-resources, чтобы закрыть их
0 голосов
/ 10 апреля 2019
 public static void compressListFiles(List<Pair<String, String>> filePairList, ZipOutputStream zipOut) throws Exception {
    for (Pair<String, String> pair : filePairList) {
        File fileToZip = new File(pair.getValue());
        String fileId = pair.getKey();
        FileInputStream fis = new FileInputStream(fileToZip);
        ZipEntry zipEntry = new ZipEntry(fileId + "/" + fileToZip.getName());
        zipOut.putNextEntry(zipEntry);
        byte[] bytes = new byte[1024];
        int length;
        while ((length = fis.read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
        fis.close();
    }
}
0 голосов
/ 10 апреля 2019

попробуйте следующее решение:

private byte[] zipFiles(ArrayList<File> files, String[] Ids) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream zos = new ZipOutputStream(baos);
    for (File file : files) {
        ZipEntry ze= new ZipEntry(file.getName());
        zos.putNextEntry(ze);

        FileInputStream fis = new FileInputStream(file);

        int len;
        while ((len = fis .read(buffer)) > 0) {
            zos.write(buffer, 0, len);
        }

        fis .close();
    }
    byte[] byteArray = baos.toByteArray();
    zos.flush();
    baos.flush();
    zos.close();
    baos.close();

    return byteArray;
}

ИДК, почему вы ставите count переменную и почему вы ставите дважды zos.putNextEntry(new ZipEntry()).

...