我想使用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) {
}
}
非常感谢任何帮助。
答案 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之前 一次将所有项目读入堆中。
(请注意,这是一个伪代码,您可能还想添加更好的处理方式并完善其他内容。)