Axios onUploadProgress和onDownloadProgress不支持CORS

我有一个用Node.js编写的服务器,一个在浏览器中运行的Web客户端。客户端应向服务器上传和下载一些文件。服务器不是最初提供客户端的服务器,所以我们这里有跨域的情况。

服务器使用cors中间件实现跨域请求。由于我还使用了许多自定义标头,因此我将其与如下配置一起使用:

api.use(cors({
  origin: '*',
  allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
  exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ]
}));

相反,客户端使用axios,上传文件的代码如下所示:

await axios({
  method: 'post',
  url: 'https://localhost:3000/api/v1/add-file',
  data: content,
  headers: {
    // ...
  }
});

到目前为止,一切都按预期进行,尤其是CORS这件事。

现在我想跟踪上传的进度,因此我将onUploadProgress回调添加到axios调用中,作为suggested by its documentation:

await axios({
  method: 'post',
  url: 'https://localhost:3000/api/v1/add-file',
  data: content,
  headers: {
    // ...
  },
  onUploadProgress (progressEvent) {
    console.log({ progressEvent });
  }
});

如果我现在运行客户端,上传仍然有效-但不会调用onUploadProgress回调。我读到并不是所有的浏览器都支持它(在内部,它只绑定到底层XmlHttpRequest对象的onprogress事件),但是最新的Chrome应该能够做到这一点(如果我尝试不同的设置,在不涉及CORS的情况下,一切似乎都正常)。所以我假设这是CORS的问题。

我有read,只要您向onprogress事件添加监听程序,就会发送印前检查请求。这又意味着服务器必须接受OPTIONS请求。为此,我在服务器上添加了以下代码,前面提到的api.use调用:

api.options('*', cors({
  origin: '*',
  methods: [ 'GET', 'POST' ],
  allowedHeaders: [ 'content-type', 'authorization', 'x-metadata', 'x-to' ],
  exposedHeaders: [ 'content-type', 'content-disposition', 'x-metadata' ],
  optionsSuccessStatus: 200
}));

结果:上传仍然有效,但是仍然没有引发onUploadProgress事件。所以我假设我错过了一些与CORS相关的东西。问题是:我错过了什么?

我还尝试使用更大的文件(几乎是2 GB而不是几KB)重新运行相同的设置,但结果是相同的。有人知道我做错了什么吗?

更新

回复部分评论:首先,我的axios版本是0.18.0,所以我使用的是可用的最新稳定版本。

服务器的源代码可以在存储库thenativeweb/wolkenkit-depot中找到。CORS部件配置here。请注意,这与我在上面发布的版本略有不同,因为我做了一些本地更改。但是,这是您需要处理的地方。

要构建服务器,您需要安装Node.js和Docker。然后,运行:

$ npm install
$ npx roboter build

这将生成一个新的Docker镜像,其中包含服务器的代码(您需要此Docker镜像才能运行下面的客户端测试)。请注意,如果您更改了服务器上的任何内容,则必须再次重建此Docker映像。

客户端可以在存储库中thenativeweb/wolkenkit-depot-client-js的分支issue-145-notifiy-about-progress-when-uploading-or-downloading-files中找到。

在文件DepotClient.js中,我已经向onUploadProgress添加了一个事件侦听器,该侦听器应该只抛出一个异常(进而应该非常明显地表明该事件已引发)。

然后由this test运行此代码(对此有更多测试,但这是其中之一),这应该会导致异常,但它没有。

要再次运行测试,您必须安装Node.js和Docker,并且必须按照前面所述构建服务器。然后使用以下命令代表您运行测试:

$ npm install
$ npx roboter test --type integration

我的期望是至少有一个addFile测试应该归档。事实是,它们都变成了绿色,这意味着上传本身工作得很好,但是onUploadProgress事件永远不会被引发。如果你想知道测试是在什么环境下进行的:我在这里使用的是木偶人,这是一个无头的Chrome。


解决方案

由于该代码工作得非常好(无论有没有CORS),我将假定您使用的是旧的AXIOS版本。onUploadProgress已添加到版本0.14.0

如果您使用的是旧版本,可以使用:progress而不是onUploadProgress,或者只更新到最新版本。

0.14.0(2016-08-27)

将拆分progress事件处理程序拆分为onUploadProgressonDownloadProgress(#423)

await axios({
  method: 'post',
  url: 'https://localhost:3000/api/v1/add-file',
  data: content,
  headers: {
    // ...
  },
  // Old version
  progress (progressEvent) {
    console.log({ progressEvent });
  },
  // New version
  onUploadProgress (progressEvent) {
    console.log({ progressEvent });
  }
});

更新。

提供的代码在浏览器上运行得很好,但在node上运行时就不行了,因为在Node.js中运行axios时,它使用的是/lib/adapters/http.js,而不是/lib/adapters/xhr.js

如果您阅读该代码,您将看到http适配器上没有任何onUploadProgress选项。

我在浏览器中测试了您的代码,命中了您的端点,它工作得很好。问题出在您运行从Node.js运行的集成测试时。

这里您有一个问题:https://github.com/axios/axios/issues/1966他们建议您使用:progress-stream如果您想要有进度,但onUploadProress函数不会触发它。

更新2

我可以确认在puppeteer上运行测试时,它使用的是XMLHttpRequest处理程序,而不是Node.js。问题是您在异步回调中抛出错误,try/catch没有捕获该错误。

try {
  await request({
    method: 'post',
    url: `${protocol}://${host}:${port}/api/v1/add-file`,
    data: content,
    headers,
    onUploadProgress (progress) {
      // This error occurs in a different tick of the event loop
      // It's not catched by any of the try/catchs that you have in here
      // Pass a callback function, or emit an event. And check that on your test
      throw new Error(JSON.stringify(progress));
    }
  });

  return id;
} catch (ex) {
  if (!ex.response) {
    throw ex;
  }

  switch (ex.response.status) {
    case 401:
      throw new Error('Authentication required.');
    default:
      throw ex;
  }
}

如果您想要捕获它,则必须将axios调用包装在Promise中并在其中拒绝(这没有意义,因为这仅用于测试)。

因此,我建议将onUploadProgress参数传递给addFile,这样您就可以检查它是否从测试到达onUploadProgress

若要查看是否正在调用:throw new Error(JSON.stringify(progress));,请使用{ headless: false }启动木偶。

这样您将看到错误被正确触发。

XMLHttpRequestUpload.onUploadProgress

相关文章