将包含多个文件的 Inputstream 放入一个 ZipEntry

Put Inputstream containing multiple files into one ZipEntry

我想将 File 的数组压缩到一个 zip 文件并将其发送到浏览器。每个FileInputstream是一个shapefile,实际上由多个文件(.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();
    }

尝试以下解决方案:

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;
}

IDK 为什么要放置 count 变量以及为什么放置两次 zos.putNextEntry(new ZipEntry())

根据您的代码,您的 files 数组中的每个文件似乎都已经是一个 zip 文件

当您稍后执行 zipFiles 时,您正在制作一个 zip 文件,其文件夹中包含更多 zip 文件。您显然不想要这个,但您想要一个 zip 文件,其中包含包含所有可能 zip 文件内容的文件夹。

基于位于“Reading data from multiple zip files and combining them to one”的 Thanador 的现有答案,我设计了以下解决方案,还包括目录和适当的流处理:

/**
 * 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 来关闭它们
 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();
    }
}