【问题标题】:YES or NO: Can a server send an HTTP response, while still uploading the file from the correlative HTTP request?是或否:服务器能否发送 HTTP 响应,同时仍从相关的 HTTP 请求上传文件?
【发布时间】:2011-06-15 08:26:45
【问题描述】:

如果网站用户通过以下方式提交 HTML 表单:(1) post 方法; (2) 多部分/表单数据编码类型;并且,(3)一个大附件,服务器可以上传一个posted文件,并在文件上传完成之前发送一个服务器生成的HTTP响应,而不使用AJAX?

那是相当密集的。所以,我写了一个例子来说明我的意思。假设有一个带有标题字段的图像上传表单。

  <form action="upload-with-caption/" method="post" enctype="multipart/form-data">
    <input type="hidden" id="hiddenInfo" name="hiddenInfo" />
    File:     <input type="file" name="imgFile" id="imgFile" /><br />
    Caption:  <input type="text" name="caption" id="caption" />
        <input type="submit" />
  </form>

我想将标题存储在具有定义的数据库表中:

[文件表]

  • file_id [唯一标识符]
  • file_caption [varchar(500)]
  • file_status [int]

那我想把文件上传到/root/{unique-id}/filename.ext

file_status 映射到具有以下定义的 C# 枚举:

enum  FileUploadStatus{
    Error = 0,
    Uploading = 1,
    Uploaded = 2
}

当表单提交时,如果文件太大而无法在 1 秒内处理,我想向网页发送一个回复,说明它正在上传。

我可以使用单个同步 HTTP 帖子来做到这一点吗?

注意:我显然想稍后使用 AJAX 检查状态更新,但这不是这个问题要问的。我特意问的是响应发送后文件是否可以继续上传。

【问题讨论】:

  • 您可以使用 JavaScript 轻松显示“上传”状态。真的不需要 AJAX。

标签: ajax forms file-upload xmlhttprequest http-post


【解决方案1】:

HTTP 是一种同步协议。
在收到整个请求之前,您无法发送响应。

【讨论】:

  • 这并不完全正确。您当然可以在收到单个响应之前发送多个请求:http 管道。 en.wikipedia.org/wiki/HTTP_pipelining 我相信您也可以在收到请求的标头后发送响应,但请求正文仍在进行中。如果您开始发送响应正文,预计尚未到达的请求,您只会遇到麻烦。
  • YES 和 NO :-) HTTP 服务器可以在使用整个请求实体之前提供响应(标头和实体),但仅在返回错误时提供。换句话说,在接受非错误响应时,不能要求客户端中止请求实体,只能接受错误响应。
  • 可能的情况是,您当然可以在知道发出任何类型的请求后立即发送响应,甚至在收到标头之前,但浏览器可能只会在它之前发送整个正文甚至尝试读取或解析响应。所以整个将被上传,而用于响应的 TCP 停止,这将一直持续到浏览器在发送整个文件后开始处理响应。所以 JS 无法访问响应,直到文件被完整发送。
【解决方案2】:

仅查看 HTTP 规范(RFC 的 753x),答案是(并且,当前接受的答案是错误的)。特别是 HTML,我认为没有什么可添加的。

HTTP/1.1 协议“依赖于响应到达的顺序与在同一连接上发出请求的顺序完全对应”(RFC 7230 §5.6)。时间与它无关。

该协议不仅允许早期响应,而且来自 4xx(客户端错误)和 5xx(服务器错误)类别的某些消息语义实际上希望在请求完成之前发送响应。

让我们举个例子。如果您打算向 Web 服务器发送 5 万亿字节(假设这个数字适合 Content-Length 标头使用的任何数据类型),您预计什么时候会收到“413 Payload Too Large”响应?尽快或仅在请求传输完成几十年后?显然越早越好!

2xx(成功)响应有点不同。这些响应“表明客户端的请求已成功接收、理解和接受”(RFC 7231 §6.3)。提前发回此类响应可能会使客户感到困惑。

相反,您可能希望作为早期响应发回的内容属于 1xx(信息)类别。这些被称为“临时响应”,旨在取代但不会淘汰最终响应。

RFC 7231 §6.2:

1xx(信息)类状态代码表示临时 用于传达连接状态或请求进度的响应 在完成请求的操作并发送最终结果之前 回应。

RFC 7230 §5.6:

每个请求只出现多条响应消息 当一个或多个信息响应之前 对同一请求的最终响应。

RFC 7231 §5.1.1 有一个很好的例子,客户端将要发送一个“可能很大”的消息,但不是在头部之后立即发送正文,而是客户端包含一个 Expect: 100-continue 头部,然后进入一个短暂的暂停,同时期望服务器通过响应“100 Continue”临时响应来拒绝消息或欢迎客户端继续。然后,这可能避免客户端必须白白传输字节。聪明!

最后,我认真思考了很久,我们何时想要在请求完成之前将 2xx(成功)响应发送回客户端?我只能想出一个场景——这当然不是一种常见的情况,但我会说:如果服务器已经消耗了足够多的请求以便采取行动,并且服务器希望丢弃剩余的请求正文,因为残基足够大,同时对服务器不再使用,则响应 202 Accepted 并包含“Connection: close”标头。

这显然不利于连接重用,也很容易导致客户端混淆,因此我们提前响应的回报应该是 1) 足以减轻建立新连接的开销,2) 有利足以抵消没有为早期响应做好准备的客户崩溃的危险,并且 3) 有据可查。

“Connection: close”标头将明确指示客户端停止发送请求 (RFC 7230 §6.3)。并且由于消息框架,连接无论如何都已失效,因为无法通过同一连接上的新消息交换对恢复通信。从技术上讲,客户端可以干净地中止分块传输(RFC 7230 §4.1),从而保存连接,但这是细节,不适用于一般情况。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-07
    相关资源
    最近更新 更多