4

我正在使用 XMLHttpRequest (Level 2) 将文件上传到 node.js 服务器。我正在检查文件流以获取服务器端的有效标头。现在,如果流式传输时出现任何错误,我想触发取消上传。我的 XMLHttpRequest 代码非常简单:

var xhr = new XMLHttpRequest();
var file;
$('#file').on('change', function(e) {
  file = this.files[0];
  file.filename = this.files[0].name;
});
$('#submit').on('click', function(e) {
  e.preventDefault();
  xhr.open('POST', '/upload', true);
  xhr.send(file);
});

在服务器端,我通过验证器将 req 流传输到文件流中。如果验证时出现任何错误,我希望 XMLHttpRequest 中止上传。但是仅仅发送 400 作为对请求的响应根本不起作用。我在 xhr 上注册了各种听众,但没有一个开火。它似乎根本不关心 400 并且仍然尝试上传文件。通常我希望 onreadystatechange 事件会在这种情况下触发。

我尝试通过发出 req.end() 来关闭 req,但这似乎有点苛刻,并揭示了另一种好奇心。如果我这样做,XMLHttpRequest 会重试正确发送 POST 请求 7 次(在 Chrome 中;在 Firefox 中 5 次;这是否记录在任何地方?)。

编辑:正如保罗建议的那样:

...在从服务器接收到任何状态之前连接关闭...

在服务器端,我尝试监听响应的完成事件。

  checkedFile.on('error', function (err) {
    res.status(400);
    res.end();
  });

  res.on('finish', function () {
    req.destroy();
  });

但无论如何它都会重试。我只是不在乎400。

也许还有另一种在不破坏请求流的情况下取消的方法?

第二次编辑:可以在不破坏请求流的情况下结束请求:

checkedFile.on('wrong', function (err) {
  checkedFile.end();
  res.writeHead(400,  { 'Connection': 'close' });
  res.end();
});

但 XHR 仍在重试请求。

4

2 回答 2

4

在某些情况下,如果服务器过早关闭连接,则允许客户端重试请求,这就是 Chrome 在您的示例中所做的。此行为在HTTP 1.1 规范 8.2.4中定义:

如果 HTTP/1.1 客户端发送的请求包含请求正文,但不包含期望为“100-continue”的 Expect 请求头字段,并且客户端未直接连接到 HTTP/1.1 源服务器,并且如果客户端在从服务器接收到任何状态之前看到连接关闭,客户端应该重试请求。

如果你想取消 XMLHttpRequest,你应该监听errorXHR 的事件,然后调用它的abort()方法,这样可以防止 Chrome 重新发送请求。

于 2013-11-14T10:53:55.563 回答
2

好吧,看来,一个人必须忍受这个:

当请求没有收到响应时会发生什么?我看到重试

超时 POST 请求的浏览器重试行为不一致

http://geek.starbean.net/?p=393

顺便说一句,如果您检查文件头的内容类型,您会遇到同样的问题。

于 2013-11-14T15:43:38.713 回答