如果您使用的是 Spring 3,您可以使用很多 swagger 注释接口来帮助干净地构建您的ResponseEntity,同时正确使用StreamingResponseBody对 spring 预期的格式进行原型制作。
这里的body 代码是将ZipOutputStream 流映射到控制器期望返回的StreamingResponseBody 类型的一种缩短方法(.body(out -> {...}) 在下面的代码中执行此操作)。
[控制器] 代码如下所示:
@GetMapping(value = "/myZip")
@Operation(
summary = "Retrieves a ZIP file from the system given a proper request ID.",
responses = {
@ApiResponse(
description = "Get ZIP file containing data for the ID.",
responseCode = "200",
content = @Content(schema = @Schema(implementation = StreamingResponseBody.class))),
@ApiResponse(
description = "Unauthenticated",
responseCode = "401",
content = @Content(schema = @Schema(implementation = ApiErrorResponse.class))),
@ApiResponse(
description = "Forbidden. Access Denied.",
responseCode = "403",
content = @Content(schema = @Schema(implementation = ApiErrorResponse.class)))
})
public ResponseEntity<StreamingResponseBody> myZipBuilder(@RequestParam String id, HttpServletResponse response)
throws IOException {
final String fileName = "MyRequest_" + id + "_" + new SimpleDateFormat("MMddyyyy").format(new Date());
return ResponseEntity.ok()
.header(CONTENT_DISPOSITION,"attachment;filename=\"" + fileName + ".zip\"")
.contentType(MediaType.valueOf("application/zip"))
.body(out -> myZipService.build(id, response.getOutputStream()));
}
您的服务 build 方法的代码只需要接受您对数据所需的任何参数,加上您的 ServletOutputStream responseOutputStream 参数以允许您构建您的 ZipOutputStream 对象通过那个流。
在下面的小示例中,您可以看到我在 buildDataLists 方法(未显示)中构建了一些 CSV 数据,这只是 List 的列表。然后我将每个顶层列出项目并使用我的streamWriteCsvToZip 将它们推送到ZipOutputStream 对象中。关键是,您构建了使用控制器提供的 responseOutputStream 播种的 ZIP 流。完全构建完 zip 后,请确保将其关闭(在我的情况下为 zos.close())。然后将zos 对象返回给控制器。
/**
* Get ZIP file containing datafiles for a given request id
*
* @param id of the request
* @param responseOutputStream for streaming the zip results
* @return ZipOutputStream a ZIP file stream for the contents
* @throws AccessDeniedException if user does not have access to this function
* @throws UnauthenticatedException if user is not authenticated
*/
public ZipOutputStream build(String id, ServletOutputStream responseOutputStream) throws IOException {
try {
List<List<String[]>> csvFilesContents = buildDataLists(id);
final ZipOutputStream zos = new ZipOutputStream(responseOutputStream);
streamWriteCsvToZip("control", id, zos, csvFilesContents.remove(0));
streamWriteCsvToZip("roles", id, zos, csvFilesContents.remove(0));
streamWriteCsvToZip("accounts", id, zos, csvFilesContents.remove(0));
zos.close(); // finally closing the ZipOutputStream to mark completion of ZIP file
return zos;
} catch (IOException | ClientException ex) {
throw ex;
}
}
这里没有魔法。只需将您的数据放入 zip 流中即可。在我的例子中,我正在提取列表/数组数据,将其放入 CSV,然后将该 CSV 放入 zip 中(使用 zos.putNextEntry(entry); 作为条目)。 CSV 和 ZIP 都保存为流,因此在此操作期间不会将任何内容写入文件系统,最终结果可以由控制器流式传输。确保每次将条目写入 zip 输出流 (zos.closeEntry()) 时关闭条目。
private void streamWriteCsvToZip(String csvName, String id, ZipOutputStream zos, List<String[]> csvFileContents)
throws IOException {
String filename = id + "_" + csvName + ".csv";
ZipEntry entry = new ZipEntry(filename); // create a zip entry and add it to ZipOutputStream
zos.putNextEntry(entry);
CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(zos)); // Directly write bytes to the output stream
csvWriter.writeAll(csvFileContents); // write the contents
csvWriter.flush(); // flush the writer
zos.closeEntry(); // close the entry. Note: not closing the zos just yet as we need to add more files to our ZIP
}