如何解决 CORS 跨域问题
Ydesign 在加载图片到 Fabric 画布时,默认会给每张图片加上 crossOrigin="anonymous" 属性(这是"导出画布时不被污染 / 不报错"的前提)。
但如果图片所在的服务器没有返回对应的 CORS 响应头,你就会看到这个经典错误:
Access to image at 'http://example.com/image.jpg' from origin 'http://your-domain.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
一旦出现这个错误,图片加载不上来,就算加载上来画布也会被"污染"(tainted),导致后续 store.toDataURL() / store.toBlob() / saveAsImage() 全部抛错。
本文说明如何彻底解决它。
① 让图片服务器返回 CORS 响应头(根治方案)
这是最正确、最该做的事情。只需要在服务器的响应里加上:
Access-Control-Allow-Origin: *
常见对象存储平台的配置方式
AWS S3:在 Bucket → Permissions → CORS configuration 里加:
[
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3000
}
]
详见 AWS 官方文档。
阿里云 OSS / 腾讯云 COS:控制台 → 跨域访问 CORS → 添加规则:
- 来源 Origin:
*(或你的域名列表) - 允许方法:
GET、HEAD - 允许 Headers:
*
Cloudflare R2:在 R2 Bucket 的 Settings 里加 CORS 规则,字段同上。
自建 Nginx / 后端服务器:给图片响应加头即可:
location ~* \.(png|jpg|jpeg|webp|svg|gif)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, HEAD';
}
CDN 层面
如果你的图片走 CDN,有些 CDN 会自动剥掉源站的 CORS 头,需要在 CDN 面板里显式开启"CORS 响应头透传"或直接配置 CORS 规则。Ydesign 官方默认使用的一份默认 CDN 已开启。
② 确保所有 <img> 标签都带 crossOrigin 属性
浏览器对每张图片只会建立一个缓存。如果你的页面在别处(比如素材面板的预览图、上一步的确认页)先用不带 crossOrigin 的方式加载了它,再进画布时浏览器就会复用那份"污染"的缓存,导致 crossOrigin='anonymous' 无效。
解决方法:页面上所有展示用户图片的地方都加 crossOrigin="anonymous"。
// ❌ 错误示范
<img src="http://example.com/image.jpg" />
// ✅ 正确示范
<img src="http://example.com/image.jpg" crossOrigin="anonymous" />
如果你使用 Ydesign 内置的 <ImagesGrid /> 渲染素材面板,它已经默认带了 crossOrigin="anonymous",无需额外处理。
③ 如何排查是哪张图片出了问题?
当 store.toDataURL() 抛出 tainted canvas 错误时,可以遍历所有图片检查:
import { observer } from 'mobx-react-lite';
const urls = store.objects.filter(o => o.type === 'Image').map(o => o.src);
for (const url of urls) {
try {
const res = await fetch(url, { mode: 'cors' });
const hasCors = !!res.headers.get('access-control-allow-origin');
console.log(hasCors ? '✅' : '❌', url);
} catch (e) {
console.log('❌', url, e.message);
}
}
命令行里找到问题图片之后,按 ① 的方式修复对应服务器的 CORS 配置即可。
④ 降级方案:后端代理(不得已时)
如果图片源站实在无法配置 CORS(比如是第三方 API、上游合作方),可以在自己的后端做一层代理:
// Express 示例
app.get('/proxy/image', async (req, res) => {
const target = req.query.url;
const upstream = await fetch(target);
const buffer = Buffer.from(await upstream.arrayBuffer());
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Content-Type', upstream.headers.get('content-type') || 'image/jpeg');
res.send(buffer);
});
然后在画布里使用代理 URL:
store.addElement({
type: 'Image',
src: `/proxy/image?url=${encodeURIComponent('https://third-party.com/image.jpg')}`,
});
⚠️ 代理方案会增加后端负载 + 流量成本,只在万不得已时使用。
延伸阅读
- 👉 Upload 面板 —— 把用户本地文件上传到自己的 OSS(带正确的 CORS)
- 👉 云渲染 API —— 服务端渲染时对图片 / 字体 URL 的 CORS 要求
- 👉 编辑器配置 · 自定义上传 ——
setUploadFunc接入图床