如何避免文件上传时的 CORS 预检请求?

Posted

技术标签:

【中文标题】如何避免文件上传时的 CORS 预检请求?【英文标题】:How to avoid CORS preflight request on file upload? 【发布时间】:2014-03-10 12:08:09 【问题描述】:

我正在使用 jquery-fileupload 来允许用户将文件上传到外部服务(更具体地说是 Cloudinary):

<input type='file' name='file' class='cloudinary-fileupload' 
  data-url='https://api.cloudinary.com/v1_1/wya/auto/upload' />
<script>
  $('.cloudinary-fileupload').fileupload();
</script>

由于是外部目标,浏览器会发起 CORS 请求。但是,我注意到浏览器预先设置了 CORS 预检请求。 http://www.html5rocks.com/en/tutorials/cors/ 提供了关于何时触发预检请求以及何时不触发的非常好的见解。据我所知,我的请求满足作为 CORS 简单请求的所有条件(参见 'Types of CORS requests' 部分)。

发送到外部服务的文件上传请求

POST /v1_1/wya/image/upload HTTP/1.1
Host: api.cloudinary.com
Connection: keep-alive
Content-Length: 22214
Accept: */*
Origin: http://wya.herokuapp.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarym73rCIa6t8eTNkTa
Referer: http://wya.herokuapp.com/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4,de;q=0.2

在文件上传请求之前发送到外部服务的附加预检请求

OPTIONS /v1_1/wya/image/upload HTTP/1.1
Host: api.cloudinary.com
Connection: keep-alive
Access-Control-Request-Method: POST
Origin: http://wya.herokuapp.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36
Access-Control-Request-Headers: accept, content-type
Accept: */*
Referer: http://wya.herokuapp.com/
Accept-Encoding: gzip,deflate,sdch
Accept-Language: sv-SE,sv;q=0.8,en-US;q=0.6,en;q=0.4,de;q=0.2

有没有办法避免这种额外的预检请求?据我所知,文件上传请求是一个 CORS 简单请求,因为它是一个带有 Content-Type multipart/form 的 HTTP POST -data 并且只有简单的请求 HTTP 标头。


我想摆脱额外的预检请求的原因是 Cloudinary 发送 HTTP 302/304 重定向作为对文件上传的响应。浏览器不遵循这些重定向。 Chrome 失败并显示以下消息:

XMLHttpRequest cannot load https://api.cloudinary.com/v1_1/wya/image/upload. 
The request was redirected to 'http://wya.herokuapp.com/upload?bytes=21534&created_at=2014-02-12T09%3A04%3…d5b62ebb92b9236e5be6d472df242d016&type=upload&version=1392195882&width=723', 
which is disallowed for cross-origin requests that require preflight. 

【问题讨论】:

【参考方案1】:

问题是 XHR 标头没有随请求一起发送到 Cloudinary,这导致 Cloudinary 重定向(IE 回退)而不是返回 JSON。这通常是由于小部件初始化不当引起的。您通常不需要自己致电$('.cloudinary-fileupload').fileupload(),因为这是由包含的 javascript 完成的。如果您仍需要手动初始化小部件,请使用$('.cloudinary-fileupload').cloudinary_fileupload()

【讨论】:

感谢 Tal 的良好反馈!更改为 .cloudinary_fileupload() 确实起到了神奇的作用。 Cloudinary JS documentation 声明使用 .fileupload() 有点令人困惑。 当您说“这是由包含的 JavaScript 完成的”时,您能解释一下如何吗?因为我还要添加手动调用。脚本是否应该在每个 .cloudinary-fileupload 类上添加一个侦听器?当它失败时我们可以测试它吗? jquery.cloudinary.js 在加载时调用 .cloudinary-fileupload 类的 cloudinary_fileupload。通过 javascript 添加的任何新输入字段都需要手动初始化。 今天这救了我的命。谢谢!【参考方案2】:

对于那些对 javascript fetch 感兴趣的人。这是适用于云上传的代码。下面是一个 react 组件,但 fetch 应该适用于几乎任何 javascript 代码。我也配置了一个 nginxserver,但它只通过了代理。需要注意的 imp 点是,使用 FormData 对象来设置 upload_preset 和文件参数。避免标题并做一个简单的帖子。

export const uploadImg = (id, key, blob) => 
const controller = new AbortController();
const frmData = new FormData();
const apptype = blob.type;

frmData.set('upload_preset', '<upload_preset_name>');
frmData.set('public_id', id + '_' + key);
frmData.set('tags', id);
frmData.set('file', blob, key);
frmData.set('folder', '<folder_name>');

// 请注意没有使用标题。我尝试使用他们没有工作的标题//不起作用注释掉这些标题工作。

/* 
     const headers = new Headers(
        'Access-Control-Request-Headers':'Accept, Content-Type',
        'Accept': 'multipart/form-data',
        'Content-Type': 'multipart/form-data',        

    );  */


const uri = 'https://api.cloudinary.com/v1_1/<cloud_name>/image/upload';
const req = new Request(uri, 
    signal: controller.signal,
    method: 'POST',
    // mode:'no-cors',
    // headers: headers,
    body: frmData,
);

return fetch(req);enter code here

Cloudinary 必须真正包含 curl、fetch 和 axios 的标准最佳实践代码示例。开发人员最终会浪费大量时间试图找出和处理所有与 cors 相关的问题。

【讨论】:

以上是关于如何避免文件上传时的 CORS 预检请求?的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 Ng2文件上传CORS问题

如何避免单页应用程序中的 CORS 预检请求?

XMLHttpRequest CORS 到 Google Cloud Storage 仅在预检请求中工作

Laravel + Plupload 上传到 S3 响应以进行预检无效 - CORS

使用 CORS 避免预检 OPTIONS 请求

请求太重时出现 NodeJS CORS 错误(文件上传)