【问题标题】:How to completely abort the output stream download?如何完全中止输出流下载?
【发布时间】:2021-12-27 13:55:27
【问题描述】:

我们目前正在开发一项服务,该服务将归档数据并将其以ZipOutputStream 的形式返回给用户。我们目前正在寻找的是在服务器端出现问题时完全终止操作的选项。对于我们当前的实现(仅关闭响应输出流),错误会导致用户端的 zip 格式错误,但在尝试解压缩之前无法判断存档是否格式错误。期望的行为类似于下载终止(例如,从浏览器的角度来看,它会导致下载不成功的指示(红十字图标或类似的东西,取决于浏览器)明确告诉用户出现问题)。我们正在使用 Spring Boot,因此任何 Java 代码示例都将不胜感激,但如果您知道负责这种行为的底层 HTTP 机制,并且可以指出正确的方向,那也将不胜感激. 这是我们目前所拥有的(输出是 Spring REST 控制器的响应输出流 (HttpServletResponse.getOutputStream()):

try (ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream)) {
    try {
        for (ZipRecordFile fileInfo : zipRecord.listZipFileOverride()) {
            InputStream fileStream = getFileStream(fileInfo.s3region(), fileInfo.s3bucket(),
            fileInfo.s3key());
            ZipEntry zipEntry = new ZipEntry(fileInfo.fileName());
            zipOutputStream.putNextEntry(zipEntry);
            fileStream.transferTo(zipOutputStream);
        }
    }
    catch (Exception e) {
        outputStream.close();
    }
}

【问题讨论】:

  • 尝试调用zipOutputStream.finish()而不是outputStream.close(),完全没有帮助,存档很好,甚至可以正确解压缩。
  • 如果发现异常,为什么不删除 zip?
  • @jhamon 不完全理解“删除拉链”的意思。在任何时候都没有要删除的 zip,只有一个 ZipOutputStream 将数据流式传输到客户端,从而在客户端生成一个 zip 存档

标签: java spring spring-boot http zipoutputstream


【解决方案1】:

没有一种(干净的)方法可以做你想做的事:

一旦您开始将 ZIP 文件写入输出流,再更改 HTTP 响应代码为时已晚。响应代码在响应开始时发送。

因此,HTTP 服务器没有适当的方法告诉 HTTP 客户端:“嘿...忽略我发送给你的 ZIP 文件,因为它已损坏”。

那么有什么替代品呢?

  1. 在服务器端,将整个 ZIP 创建为内存对象或将其写入临时文件。如果成功,请发送 2xx 响应,后跟 ZIP 数据。如果失败,请发送 4xx 或 5xx 响应。

    主要问题是您需要足够的内存或文件系统空间来保存 ZIP 文件。

  2. 重新设计您的 HTTP API,以便客户端可以发送第二个请求以检查第一个请求的响应是否包含完整的 ZIP 文件。

  3. 您也许可以利用 MIME 多部分编码;见RFC 1341。格式良好的 MIME 多部分的每个部分都有一个开始标记和一个结束标记。您可以尝试的是让您的网络应用“手动”构建包含 ZIP 的多部分流。如果它决定必须中止 ZIP,它可以直接关闭输出流而不添加所需的结束标记。

    这样做的主要问题是您依赖客户端上的 HTTP 堆栈来告诉浏览器(或其他)多部分已损坏。此外,浏览器(或其他)不得将部分(即损坏的)ZIP 文件传递​​给用户。我不确定您是否可以依靠(特定的)网络浏览器来做到这一点。

  4. 如果您在客户端通过自定义代码运行下载,您可以实现自己的封装协议。效果与 3 相同……但您不会滥用 MIME 规范。

【讨论】:

  • 感谢您的回复!是的,我们已经考虑过第一个选项,但我们不想加载内存中的所有内容,因为文件可能非常大。至于第三个选项,在我们当前的实现中,Spring 正在执行分块流作为响应。我不确定这是否是您所说的多部分流。据我了解,就像我们目前在 catch 块中所做的那样,中途关闭流会发送一个空块,指示分块响应的结束,这反过来会导致存档格式错误,但不会导致“失败”根据需要下载。
  • “多部分流”是指您将组装的输出流,该输出流由包装在 ZIP 中的文件组成,该 ZIP 包装为 MIME 多部分中的一部分。关键是该部分有一个独特的结束标记,浏览器(或其他)有可能会以与您当前看到的不同的方式检测和处理丢失的标记。实际上,如果您可以安排在(客户端)应用程序代码中处理多部分的解码,则可以确保以不同方式检测和处理它。
猜你喜欢
  • 1970-01-01
  • 2021-11-12
  • 2010-11-26
  • 2017-11-24
  • 1970-01-01
  • 1970-01-01
  • 2017-07-23
  • 2021-03-23
  • 2023-03-30
相关资源
最近更新 更多