【问题标题】:Put Inputstream containing multiple files into one ZipEntry将包含多个文件的 Inputstream 放入一个 ZipEntry
【发布时间】:2019-04-10 08:20:09
【问题描述】:

我想将File 的数组压缩成一个压缩文件并将其发送到浏览器。每个FileInputstream 是一个shapefile,实际上由多个文件(.shp、.dbf、.shx、...)组成。

当我使用以下代码仅发送一个 File 时,它可以正常工作,并返回一个包含所有所需文件的 zipfile。

发送单个文件的代码

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

【问题讨论】:

  • 尝试在文件名中添加文件扩展名:file.getName() + ".zip" 而不是file.getName()
  • 这已经指向了正确的方向。但是,使用 new ZipEntry(Ids[count] + "/") 创建的每个文件夹现在都包含一个包含所需文件的 zip 文件夹。我只想要那里的文件,而不是在 zip 中编译。

标签: java inputstream zipoutputstream


【解决方案1】:

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

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

基于位于“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 来关闭它们

【讨论】:

    【解决方案2】:

    尝试以下解决方案:

    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())

    【讨论】:

    • count 是为每个文件创建一个单独的文件夹。第一个zos.putNextEntry(new ZipEntry()) 创建文件夹,下一个是文件。你的解决方案不起作用,它和我的一样(只写一个文件而不是所有文件(shp、dbf、...),除了将它写入一个文件夹。
    • 不,这确实会创建一个无效的 zip + 它不会像我上面提到的那样创建文件夹结构。
    【解决方案3】:
     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();
        }
    }
    

    【讨论】:

    • 这不会创建所需的文件夹结构。
    • 更改了方法参数,希望它能解决您的问题
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 2012-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多