首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用StreamingResponseBody并尝试使用资源?

如何使用StreamingResponseBody并尝试使用资源?
EN

Stack Overflow用户
提问于 2022-02-02 15:28:44
回答 1查看 1.5K关注 0票数 0

我想动态地创建一个Excel表并将其返回给客户端。为了保存一些内存,我想使用StreamingResponseBody作为返回类型。我还使用try-with-resources自动关闭workbook。这是密码。

代码语言:javascript
复制
@RestController
public class ExcelController {

  @GetMapping("/hello")
  public ResponseEntity<StreamingResponseBody> excel() throws IOException {
    try (var workbook = new XSSFWorkbook();) {
      workbook.createSheet("hello world");
      return ResponseEntity.ok()
          .contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
          .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=data.xlsx")
          .body(workbook::write);
    }
  }
}

不幸的是,这不起作用,我得到了以下错误消息。

代码语言:javascript
复制
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception

java.io.IOException: Cannot write data, document seems to have been closed already
        at org.apache.poi.ooxml.POIXMLDocument.write(POIXMLDocument.java:215) ~[poi-ooxml-5.1.0.jar:5.1.0]
        at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:111) ~[spring-webmvc-5.3.13.jar:5.3.13]
        at org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:98) ~[spring-webmvc-5.3.13.jar:5.3.13]
        at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$4(WebAsyncManager.java:337) ~[spring-web-5.3.13.jar:5.3.13]
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539) ~[na:na]
        at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) ~[na:na]
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) ~[na:na]
        at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]

我已经尝试过使用我的自定义TaskExecutor,正如StreamingResponseBody所描述的那样,但这并没有真正的帮助。最后,我找到了一个可行的解决方案,但我想知道为什么第一个解决方案(包括try-with-resources和方法引用)不能工作。

代码语言:javascript
复制
@RestController
public class ExcelController {

  @GetMapping("/hello")
  public ResponseEntity<StreamingResponseBody> excel() throws IOException {
    var workbook = new XSSFWorkbook();
    workbook.createSheet("hello world");
    return ResponseEntity.ok()
        .contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=data.xlsx")
        .body(
            out -> {
              workbook.write(out);
              workbook.close();
            });
  }
}

我非常感谢你的帮助!谢谢

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-03 07:27:39

因为StreamingResponseBody工作异步,而且由于您的试用资源在该部分之外,所以资源可能已经关闭(我怀疑99.9%的情况下?)。来修复StreamingResponseBody中的所有内容。

代码语言:javascript
复制
return ResponseEntity.ok()
    .contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=data.xlsx")
    .body(
        out -> {
          try (var workbook = new XSSFWorkbook()) {
            workbook.createSheet("hello world");
            workbook.write(out);
          }
      });
}

为了使它更具可读性(以及我不喜欢多行lambda这一事实),您当然也可以将代码移到方法中,并从lambda调用它。

代码语言:javascript
复制
private void writeWorkbook(OutputStream out) {
  try (var workbook = new XSSFWorkbook()) {
    workbook.createSheet("hello world");
    workbook.write(out);
  }
}

return ResponseEntity.ok()
    .contentType(MediaType.parseMediaType("application/vnd.ms-excel"))
    .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=data.xlsx")
    .body( this::writeWorkbook);
}

这将在适当的时间打开(并关闭) Workbook

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/70958321

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档