如何在春天提供文件/ PDF文件的反应方式

时间:2017-10-09 10:05:46

标签: java spring-boot reactive spring-webflux

我有以下端点代码来提供PDF文件。

@RequestMapping
ResponseEntity<byte[]> getPDF() {
  File file = ...;
  byte[] contents = null;
  try {
    try (FileInputStream fis = new FileInputStream(file)) {
      contents = new byte[(int) file.length()];
      fis.read(contents);
    }
  } catch(Exception e) {
    // error handling
  }
  HttpHeaders headers = new HttpHeaders();
  headers.setContentDispositionFormData(file.getName(), file.getName());
  headeres.setCacheControl("must-revalidate, post-check=0, pre-check=0");
  return new ResponseEntity<>(contents, headers, HttpStatus.OK);
}

如何将上述内容转换为反应式Flux/MonoDataBuffer

我检查了DataBufferUtils,但它似乎没有提供我需要的东西。我也没有找到任何例子。

3 个答案:

答案 0 :(得分:5)

实现这一目标的最简单方法是使用Resource

@GetMapping(path = "/pdf", produces = "application/pdf")
ResponseEntity<Resource> getPDF() {
  Resource pdfFile = ...;
  HttpHeaders headers = new HttpHeaders();
  headers.setContentDispositionFormData(file.getName(), file.getName());
  return ResponseEntity
    .ok().cacheControl(CacheControl.noCache())
    .headers(headers).body(resource);
}

请注意,DataBufferUtils有一些有用的方法可以将InputStream转换为Flux<DataBuffer>,例如DataBufferUtils#read()。但处理Resource仍然是优越的。

答案 1 :(得分:0)

我遇到相同的问题。

我使用Webflux Spring WebClient

我写风格RouterFunction

我在下面的解决方案

ETaxServiceClient.java

final WebClient defaultWebClient;


public Mono<byte[]> eTaxPdf(String id) {
    return defaultWebClient
            .get()
            .uri("-- URL PDF File --")
            .accept(MediaType.APPLICATION_OCTET_STREAM)
            .exchange()
            .log("eTaxPdf -> call other service")
            .flatMap(response -> response.toEntity(byte[].class))
            .flatMap(responseEntity -> Mono.just(Objects.requireNonNull(responseEntity.getBody())));
}

ETaxHandle.java

@NotNull
public Mono<ServerResponse> eTaxPdf(ServerRequest sr) {
    Consumer<HttpHeaders> headers = httpHeaders -> {
        httpHeaders.setCacheControl(CacheControl.noCache());
        httpHeaders.setContentDisposition(
                ContentDisposition.builder("inline")
                        .filename(sr.pathVariable("id") + ".pdf")
                        .build()
        );
    };
    return successPDF(eTaxServiceClient
            .eTaxPdf(sr.pathVariable("id"))
            .switchIfEmpty(Mono.empty()), headers);
}

ETaxRouter.java

@Bean
public RouterFunction<ServerResponse> routerFunctionV1(ETaxHandle handler) {
    return route()
            .path("/api/v1/e-tax-invoices", builder -> builder
                    .GET("/{id}", handler::eTaxPdf)
            )
            .build();
}

CommonHandler.java

Mono<ServerResponse> successPDF(Mono<?> mono, Consumer<HttpHeaders> headers) {
    return ServerResponse.ok()
            .headers(headers)
            .contentType(APPLICATION_PDF)
            .body(mono.map(m -> m)
                    .subscribeOn(Schedulers.elastic()), byte[].class);
}

结果:已成功显示在浏览器中。

为我工作。

enter image description here

答案 2 :(得分:-1)

下面是将附件作为字节流返回的代码:

@GetMapping(
        path = "api/v1/attachment",
        produces = APPLICATION_OCTET_STREAM_VALUE
)
public Mono<byte[]> getAttachment(String url) {
    return rest.get()
            .uri(url)
            .exchange()
            .flatMap(response -> response.toEntity(byte[].class));
}

这种方法非常简单,但是缺点是它将整个附件加载到内存中。如果文件较大,将是一个问题。

要克服这一点,我们可以使用DataBuffer来分块发送数据。这是一种有效的解决方案,适用于任何大文件。下面是使用DataBuffer修改的代码:

@GetMapping(
        path = "api/v1/attachment",
        produces = APPLICATION_OCTET_STREAM_VALUE
)
public Flux<DataBuffer> getAttachment(String url) {
    return rest.get()
            .uri(url)
            .exchange()
            .flatMapMany(response -> response.toEntity(DataBuffer.class));
}

通过这种方式,我们可以以响应方式发送附件。