首页 > 解决方案 > Vertx 将 zip 文件流式传输到 http 响应

问题描述

我正在尝试流式传输一个 zip 文件作为输出供用户下载。我创建了一个 zipOutputStream,并尝试将其发送到 vertx http 响应中。

但是,这会下载损坏的 zip 文件或挂起

 public void download(RoutingContext routingContext) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ZipOutputStream zos = new ZipOutputStream(baos)) {

      for (int i = 0; i < 2; i++) {
        String fileName = "test" + i + ".csv";
        File tempFile = createTempFile("test" + i);
        tempFile.deleteOnExit();

        InputStream is = new FileInputStream(tempFile);
        byte[] buffer = new byte[is.available()];
        is.read(buffer);

        ZipEntry entry = new ZipEntry(fileName);
        zos.putNextEntry(entry);

        try {
          zos.write(buffer, 0, buffer.length);
          zos.closeEntry();

          String b64String = Base64.encodeBase64String(baos.toByteArray());

          routingContext.response()
            .setChunked(true)
            .putHeader(HttpHeaders.CONTENT_TYPE, "application/zip")
            .putHeader("Content-Disposition", "attachment; filename=\"test.zip\"")
            .putHeader(HttpHeaders.TRANSFER_ENCODING, "chunked")
            .putHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(b64String.length()));

          routingContext.response().write(b64String);
          routingContext.response().end();
          routingContext.response().close();

        } finally {
          baos.close();
        }
      }

    }
    catch(Exception ex) {

    }

  }

标签: javavert.x

解决方案


对于非常大的文件,您可以使用流式传输来避免将它们完全加载到 RAM 中:

AsyncInputStream fileContent = new AsyncInputStream(routingContext.vertx(), fileInputStream);
fileContent.endHandler((ev) -> {
    routingContext.response().end();        
});
Pump.pump(fileContent, routingContext.response()).start();

不幸的是,AsyncInputStream(InputStream 到 ReadStream 的包装器)还不是 VertX 框架的一部分,因此您必须自己实现它或找到一些开源实现。


推荐阅读