google-chrome - XMLHttpRequest CORS 到 Google Cloud Storage 仅适用于预检请求

标签 google-chrome xmlhttprequest cors google-cloud-storage

我使用发送到服务器端创建的可恢复上传 url 的 XMLHttpRequest 实现了基于浏览器的可恢复上传到 Google 的云存储。这在禁用网络安全时完全正常,我在开发过程中这样做了。

但现在在现实世界中,CORS 不断制造麻烦。我也用其他浏览器尝试过这个(没有成功),但坚持使用 chrome 进行进一步测试。

注:A fake.host进入 /etc/HOSTS用于诱使 chrome 避免 localhost 限制。尽管如此,我们在线测试服务器的“真实”域也会发生同样的情况。

请求是使用普通的 XMLHttpRequest 调用启动的:

var xhr = this.newXMLHttpRequest();
xhr.open('PUT', url, true);

xhr.setRequestHeader('Content-Type', this.currentInputFile.type);
xhr.setRequestHeader('Content-Range', 'bytes ' + startByte + '-' + (this.currentInputFile.size - 1) + '/' + this.currentInputFile.size);

xhr.onload = function(e) {
   ...
};

...

if (startByte > 0) {
    xhr.send(this.currentInputFile.slice(startByte));
} else {
    xhr.send(this.currentInputFile);
}

然后浏览器成功发起预检请求:
Remote Address:173.194.71.95:443
Request URL:https://www.googleapis.com/upload/storage/v1/b/my-bucket-name/o?uploadType=resumable&name=aa%20spacetestSMALL_512kb.mp4&upload_id=XXXXXXXXX
Request Method:OPTIONS
Status Code:200 OK

请求头:
:host:www.googleapis.com
:method:OPTIONS
:path:/upload/storage/v1/b/my-bucket-name/o?uploadType=resumable&name=aa%20spacetestSMALL_512kb.mp4&upload_id=XXXXXXXXX
:scheme:https
:version:HTTP/1.1
accept:*/*
accept-encoding:gzip,deflate
accept-language:en-US,en;q=0.8,de;q=0.6
access-control-request-headers:content-range, content-type
access-control-request-method:PUT
origin:https://fake.host
referer:https://fake.host/upload.xhtml
user-agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36
x-client-data:YYYYYY

查询字符串参数
uploadType:resumable
name:aa spacetestSMALL_512kb.mp4
upload_id:XXXXXXXXX

响应头
access-control-allow-credentials:true
access-control-allow-headers:content-range, content-type
access-control-allow-methods:PUT
access-control-allow-origin:https://fake.host
alternate-protocol:443:quic
content-length:0
content-type:text/html; charset=UTF-8
date:Fri, 05 Sep 2014 14:11:21 GMT
server:UploadServer ("Built on Aug 18 2014 11:58:36 (1408388316)")
status:200 OK
version:HTTP/1.1

...并开始 PUT 请求,直到传输完所有数据。但是之后 chrome 会在没有完成/结束请求的情况下默默记录一个错误:

XMLHttpRequest 无法加载 https://www.googleapis.com/upload/storage/v1/b/my-bucket-name ……XXXXXXXX。请求的资源上不存在“Access-Control-Allow-Origin” header 。起源' https://fake.host ' 因此不允许访问。

这是关于 PUT 请求的 chrome 日志:
Request URL:https://www.googleapis.com/upload/storage/v1/b/my-bucket-name/o?uploadType=resumable&name=aa%20spacetestSMALL_512kb.mp4&upload_id=XXXXXXXXX

请求头
Provisional headers are shown
Content-Range:bytes 0-3355302/3355303
Content-Type:video/mp4
Origin:https://fake.host
Referer:https://fake.host/upload.xhtml
User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.94 Safari/537.36
X-DevTools-Emulate-Network-Conditions-Client-Id:YYYYYYY

查询字符串
uploadType:resumable
name:aa spacetestSMALL_512kb.mp4
upload_id:XXXXXXXXX

值得注意的是,当在 http://client.cors-api.appspot.com/client 中添加相同的 url 时并发出任何请求,除了 OPTIONS请求类型也失败。似乎云存储 api 只为 OPTION 请求发出正确的响应 header ,而不是 PUT/POST/GET/... 请求。

所以我在做一些不可能的事情吗?有东西坏了吗?这是云存储api中的错误吗?我花了几个小时谷歌搜索和阅读 SO 答案,到目前为止没有任何运气。

现在,我可以定期检查下载是否传输了 100% 的数据并忽略 http 请求结果,因为文件实际上已完全上传到存储桶。但这是一个相当丑陋的解决方法,如果真正的问题可以解决,我真的不想使用它。

最佳答案

当请求可恢复的上传 url 时,您必须包含浏览器在尝试使用该上传 url 时将发送的来源,否则后续上传将失败,就像问题中发生的一样(OPTIONS 调用看起来不错,但 PUT不会)。

它必须与浏览器的原点完全匹配(您可以在 javascript 中作为 location.origin 获取)。

这是本文档中“启动可恢复上传 session ”的步骤:
https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload

如果您在服务器端请求可恢复上传 url,您可能需要客户端(浏览器)将其来源(例如:location.origin)传递给您。

fwiw 我在这一步中使用了 Google 的 Cloud Storage 库 for python,并且需要像这样添加原点:

myblob.create_resumable_upload_session(mycontenttype, origin=browserorigin)

请注意,您绝对不需要为您的存储桶设置 CORS。

关于google-chrome - XMLHttpRequest CORS 到 Google Cloud Storage 仅适用于预检请求,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25688608/

相关文章:

javascript - Binance API 和 angular 4 httpClient

android - 在 Android 上的 Chrome 中获取物理屏幕尺寸/dpi/像素密度

css - img {最大高度 :%} fails in chrome

php - 为什么 jqXHR.responseText 返回我的 PHP 文件而不执行脚本?

javascript - 在 Web API Core 和 Angular JS 中启用 CORS

firefox - 如何配置 Amazon S3 CORS 以便 Firefox 加载远程托管的网络字体?

google-chrome - 用于 chrome 模拟设备的 OnePlus 3 和 Oneplus 5 视口(viewport)大小

google-chrome - Google Chrome 可以在具有无效 SSL 证书的本地开发服务器上使用吗?

Java 摘要式身份验证 POST XML

javascript - XMLHttpRequest 发布 HTML 表单