JaxRS从服务器创建并返回zip文件

时间:2012-05-26 02:52:08

标签: java jax-rs

我想使用JaxRS从我的服务器创建并返回一个zip文件。我不认为我想在服务器上创建一个实际的文件,如果可能的话我想动态创建zip并将其传递回客户端。如果我在运行时创建一个巨大的zip文件,如果zip文件中有太多文件,我的内存是否会耗尽?

此外,我不确定最有效的方法。这就是我的想法,但是当涉及到java中的输入/输出时,我非常生疏。

public Response getFiles() {

// These are the files to include in the ZIP file       
String[] filenames = // ... bunch of filenames

byte[] buf = new byte[1024];

try {
   // Create the ZIP file
   ByteArrayOutputStream baos= new ByteArrayOutputStream();
   ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(baos));

   // Compress the files
   for (String filename : filenames) {
       FileInputStream in = new FileInputStream(filename);

       // Add ZIP entry to output stream.
       out.putNextEntry(new ZipEntry(filename));

       // Transfer bytes from the file to the ZIP file
       int len;
       while ((len = in.read(buf)) > 0) {
         out.write(buf, 0, len);
       }
       // Complete the entry
       out.closeEntry();
       in.close();
   }

   // Complete the ZIP file
   out.close();

   ResponseBuilder response = Response.ok(out);  // Not a 100% sure this will work
   response.type(MediaType.APPLICATION_OCTET_STREAM);
   response.header("Content-Disposition", "attachment; filename=\"files.zip\"");
   return response.build();

} catch (IOException e) {       
}

}

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

有两种选择:

1-在时态目录中创建ZIP,然后转储到客户端。

2-在您创建zip时,使用Response中的OutputStream将zip直接发送到客户端。

但永远不要使用内存来创建巨大的ZIP文件。

答案 1 :(得分:0)

在将文件提供给客户端之前,无需从内存的第一个字节到最后一个字节创建ZIP文件。另外,也不需要提前在temp目录中创建这样的文件(特别是因为IO可能真的很慢)。

关键是开始流式传输“ ZIP响应”并在广告投放中生成内容。

假设我们有一个aMethodReturningStream(),它返回一个Stream,并且我们想将每个元素转换为ZIP文件中存储的文件。而且我们不想一直将每个元素的字节存储在任何中间表示形式(例如集合或数组)中。

那么这样的伪代码可能会有所帮助:

@GET
@Produces("application/zip")
public Response generateZipOnTheFly() {
    StreamingOutput output = strOut -> {
        try (ZipOutputStream zout = new ZipOutputStream(strOut)) {
            aMethodReturningStream().forEach(singleStreamElement -> {
                try {
                    ZipEntry zipEntry = new ZipEntry(createFileName(singleStreamElement));
                    FileTime fileTime = FileTime.from(singleStreamElement.getCreationTime());
                    zipEntry.setCreationTime(fileTime);
                    zipEntry.setLastModifiedTime(fileTime);
                    zout.putNextEntry(zipEntry);
                    zout.write(singleStreamElement.getBytes());
                    zout.flush();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            });
        }
    };
    return Response.ok(output)
                   .header("Content-Disposition", "attachment; filename=\"generated.zip\"")
                   .build();
}

此概念依赖于将StreamingOutput传递给Response构建器。 StreamingOutput不是在发送响应之前生成的完整响应/实体/正文,而是一种用于生成字节流动态(此处包裹为{{1 }}。如果您对此不确定,则可以接下来在ZipOutputStream上设置一个断点,并使用例如来观察下载进度。 flush()。 这里要记住的关键是,这里的流不是预先计算或预取的项目的“包装器”。 必须是动态的,例如包装数据库游标或类似的东西。同样,它可以被任何流数据替代。这就是为什么它不能是wget循环遍历foreach数组(每个Element[] elems的所有字节都在“内部”)的原因,例如

Element

如果您想避免在流传输ZIP之前 一次将所有项目读入堆中。

(请注意,这是一个伪代码,您可能还想添加更好的处理方式并完善其他内容。)