使用 CORS 的跨域分块上传

Posted

技术标签:

【中文标题】使用 CORS 的跨域分块上传【英文标题】:Cross-domain chunked uploads using CORS 【发布时间】:2013-01-29 09:30:46 【问题描述】:

我尝试以 10 MB 的块上传用户提交的文件。我目前正在使用原始 XMLHttpRequest(和 XDomainRequest)在前端推送每个单独的切片(File.prototoype.slice)。后端是使用upload module的nginx

仅供参考,这里是我如何使用slice的概要:

element.files[0].slice(...)

我了解跨浏览器前缀方法webkitSlicemozSlice 等等。

我遇到的问题是实际发出跨域请求。我正在从server.local 上传到upload.server.local。在 Firefox 中,options 请求正常,然后实际的post 失败。在 Chrome 和 Opera 中,options 请求失败并显示

 OPTIONS https://URL Resource failed to load

这里是来自 Firefox 的标题:

请求标头

OPTIONS /path/to/asset HTTP/1.1
Host: upload.server.local:8443
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Origin: https://server.local:8443
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-disposition,content-type,x-content-range,x-session-id
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache

响应标头

HTTP/1.1 204 No Content
Server: nginx/1.2.6
Date: Wed, 13 Feb 2013 03:27:44 GMT
Connection: keep-alive
access-control-allow-origin: https://server.local:8443
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Headers: x-content-range, origin, content-disposition, x-session-id, content-type, cache-control, pragma, referrer, host
access-control-allow-credentials: true
Access-Control-Max-Age: 10000

实际的post 请求永远不会离开浏览器。 Nginx 访问日志永远不会看到post。浏览器出于某种原因将其停止。如何解开此帖子被屏蔽的原因?

Chromium 24
Firefox 18
Opera 12.14

我已验证所有浏览器都正确支持 CORS here。

通过将我的上传指向https://cors-test.appspot.com/test,我已经确认问题肯定出在服务器端标头上。

【问题讨论】:

如果您在这两个站点上都使用 HTTP 而不是 HTTPS 会发生什么? 【参考方案1】:

如果preflight check 没有返回足够的权限,POST 不会离开浏览器,因此 POST 请求没有得到完全授权。问题中包含的请求/响应对我来说确实足够了。

您确定要在 XMLHttpRequest 中设置withCredentials = true 吗? 您确定您的服务器上有有效的(非自签名的)SSL 证书吗?即使您添加了使用无效证书浏览站点的例外,HTTPS 也可能无法通过 CORS 检查。 您是否尝试过清空缓存?您在响应标头中设置了Access-Control-Max-Age: 10000。也就是将近3个小时。我知道您在这方面的工作时间比这更长,但在特别是测试时,请将标头设置为零,这样您就不会因浏览器缓存旧访问权限而发疯。

一般来说,我会从尽可能宽松的 CORS 标头开始,然后慢慢提高安全性,看看它失败的地方。然而,这并不完全简单。比如根据MDN documentation on CORS,

在响应凭据请求时,服务器必须指定域,并且不能使用通配符。如果标头被通配符为:Access-Control-Allow-Origin: *

,上述示例将失败

当我将您问题的请求部分发送到 https://cors-test.appspot.com/test 时,我会收到以下信息:

HTTP/1.1 200 OK
Cache-Control: no-cache
Access-Control-Allow-Origin: https://server.local:8443
Access-Control-Allow-Headers: content-disposition,content-type,x-content-range,x-session-id
Access-Control-Allow-Methods: POST
Access-Control-Max-Age: 0
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Expires: Fri, 01 Jan 1990 00:00:00 GMT
Content-Type: application/json
Content-Encoding: gzip
Content-Length: 35
Vary: Accept-Encoding
Date: Thu, 23 May 2013 06:37:34 GMT
Server: Google Frontend

因此,您可以从那里开始并添加越来越多的安全性,直到它崩溃以找出罪魁祸首。

【讨论】:

是的,我确实将withCredentials 设置为true。证书也有效。 尝试允许 GET 和 OPTIONS。 我已经全部添加了。我也在发帖。 我知道这听起来很糟糕,但你清除缓存了吗?我不知道是否在 Firebug 中显示了 CORS 飞行前的缓存命中。 很多很多很多次。我仍然很欣赏这个建议。你永远不知道。

以上是关于使用 CORS 的跨域分块上传的主要内容,如果未能解决你的问题,请参考以下文章

使用 JSONP 或 CORS 的跨域 JavaScript 调用

使用 Angular 和 NodeJs (CORS) 的跨域请求

带 Cors 的跨域请求

使用 CORS 的 OPTIONS 请求宁静的跨域

Angular 或 Angular 6 中的跨域资源共享 (CORS)。在 localhost 上使用不同端口进行跨域调用时出现问题

CORS - 没有 JSONP 的跨域 AJAX 通过允许服务器上的 Origin