Spring webflux流动态构造大zip文件

问题描述 投票:0回答:1

我想使用 spring webflux 创建一个端点,在其中创建一个包含多个文件的 zip 文件。我也根据一些数据库查询动态创建这些文件。由于这个 zip 文件可能很大,我想立即将处理后的数据流式传输到客户端,而不是构建整个 zip 文件,然后返回它。我该如何实现这一目标?对于 HttpServletResponse 的注入,有多种 MVC 解释,但 Spring Webflux 中没有这样的东西。这是我到目前为止所拥有的,但它只是下载了一个“response.html”文件,即使我将其修改为“response.zip”,该文件也已损坏。

    @GetMapping(path = "/test", produces = "application/octet-stream")
    public Mono<ResponseEntity<FileSystemResource>> test(
        @ApiIgnore ServerHttpRequest request,
        @ApiIgnore ServerHttpResponse httpResponse
    ) {
        try {
            String str = "Hello";
            byte[] strToBytes = str.getBytes();
            FileSystemResource zipFile = new FileSystemResource("result.zip");
            ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile.getFile()));
            ZipEntry zipEntry = new ZipEntry("1/Hello.txt");
            out.putNextEntry(zipEntry);
            out.write(strToBytes);

            out.close();
            return Mono.just(ResponseEntity.ok()
                .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=result.zip")
                .contentType(MediaType.APPLICATION_OCTET_STREAM)
                .body(zipFile));
        } catch (Exception e) {
            e.printStackTrace();
            return Mono.empty();
        }
    }
    ```
spring stream zip spring-webflux large-files
1个回答
0
投票

您可以创建一个通量发射器,该发射器在发射器完成之前保持活动状态。

下面是使用

Resource
的示例方法,但这可以轻松地从外部源更新为
inputStream
Flux<DataBuffer

需要注意的是,要避免

public class ZipDataBuffer {

    final DefaultDataBufferFactory factory = new DefaultDataBufferFactory();
    final DefaultDataBuffer buffer = factory.allocateBuffer(1024);
    final ZipOutputStream zos = new ZipOutputStream(buffer.asOutputStream());

    public static Flux<DataBuffer> toFlux(Resource... resources) {
        var zipDataBuffer = new ZipDataBuffer();
        return Flux.create(emitter -> Flux.fromArray(resources)
                .flatMap(zipDataBuffer::write)
                .doFinally(s -> {
                    emitter.next(zipDataBuffer.finish());
                    emitter.complete();
                })
                .subscribe(emitter::next));
    }

    public Mono<DataBuffer> write(Resource resource) {
        var dataBufferFlux = DataBufferUtils.read(resource, factory, 1024);
        return DataBufferUtils.join(dataBufferFlux)
                .doOnNext(dataBuffer -> write(resource.getFilename(), dataBuffer))
                .thenReturn(read());
    }

    @SneakyThrows
    private void write(String filename, DataBuffer dataBuffer) {
        try (var is = dataBuffer.asInputStream()) {
            var zipEntry = new ZipEntry(filename);
            zos.putNextEntry(zipEntry);
            zos.write(is.readAllBytes());
            zos.closeEntry();
        }
    }

    private DataBuffer read() {
        return buffer.split(buffer.writableByteCount());
    }

    @SneakyThrows
    public DataBuffer finish() {
        zos.finish();
        zos.close();
        return read();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.