HTTP是否允许服务器在消耗所有输入之前启动输出?如果是这样,如何用Java访问这样的服务器?

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

注:此问题与Is it acceptable for a server to send a HTTP response before the entire request has been received?密切相关,不同之处在于(1)我没有发送错误,我发送了200 OK,(2)我同时控制了客户端和服务器,因此不必太在乎浏览器支持。

Context:我正在实现Java HTTP客户端和服务器来管理文件。特别地,“上传”查询包含文件路径和文件主体,并且服务器以文件的数字标识符响应。但是,如果已经上传了具有相同路径的文件,则服务器将仅使用先前生成的标识符进行响应。

Concretely:如果我按如下方式编写服务器(sparkjava)

put(url, (req, res) -> {
  Item existing = lookForExistingItem(req);
  if (existing != null) {
     return existing.getId();
  }
  /* Otherwise, consume input, save, generate id and return that */
});

...,然后服务器将以id响应并在客户端完成发送数据之前关闭连接。如果我按如下方式编写客户端:

final HttpURLConnection connection = (HttpURLConnection) new URL(...).openConnection();
connection.setDoOutput(true);
connection.setRequestMethod("PUT");
ByteStreams.copy(fileInput, connection.getOutputStream());
final String response = CharStreams.toString(new InputStreamReader(connection.getInputStream()));

然后由于关闭的连接在IOException操作期间抛出了copy。此后,我将无法再访问连接的InputStream

我的问题:如何进行这项工作?如果我更改服务器以消耗整个输入并将其丢弃,它就可以工作,但是感觉就像浪费资源(某些上传的文件可能是重达数百兆的视频)。有什么方法可以更改客户端代码以应对这种情况?

java http urlconnection
1个回答
1
投票

假设文件足够大,并且与传输部分文件相比,发出多个请求所消耗的资源要少得多,因此您可以将该调用拆分为几个请求。

enum UploadStatus {
   INITIALIZED,
   STARTED,
   UPLOADED,
   ERROR
}

我的建议:

  1. 具有静态映射ConcurrentMap<File name string, UploadStatus>(或DB条目),您可以在其中跟踪文件的上传状态
  2. 创建端点以检查和设置文件状态
  3. 客户端首先向上述端点发出请求
    • 如果文件在地图上存在且其状态不是UploadStatus.ERROR,请将文件在地图上的状态设置为UploadStatus.INITIALIZED,并让客户端(客户端A)知道它可以上传文件(应在synchronized上执行此操作块)
  4. 如果文件存在且为UploadStatus.INITIALIZED,请让该客户端(客户端B)知道其正在上传。对于UX,可以使客户端B轮询文件状态,直到UploadStatus变为ERRORUPLOADED,然后采取适当的措施。即
    • UploadStatus.ERROR上重新上传文件
    • UploadStatus.UPLOADED上显示上传的消息
  5. 一旦服务器从客户端A收到上传实际文件的请求,请保持文件上传状态为最新状态,以便在出错时其他客户端(例如客户端B)可以重新上传失败的文件。

进行文件状态检查并在单个同步块上进行设置对于避免在设置正确的文件状态时出现竞争状态很重要。另外,该枚举仅用于解释一般的高级步骤。由于您已经拥有番石榴,因此可以将Guava Cache与时基逐出一起用于存储文件雕像。

© www.soinside.com 2019 - 2024. All rights reserved.