我们写此信是为了告知您即将发生的一项变更,该变更可能会影响您从我们平台访问和使用资源的方式。
我们将对所有生成的文件 URL 强制执行 CORS 策略。这意味着我们平台托管的所有资源都将需要对跨域请求进行适当处理,或者换句话说,您不能直接使用 Tripo API 返回的链接来提供生成的模型文件。
此变更旨在提高安全性并防止未经授权地访问不同来源的资源。CORS(跨域资源共享)是一种允许服务器指定哪些外部域可以访问资源的机制。
有关 CORS 的更多信息,您可以参考 MDN 的指南。
目前,我们不为文件 URL 提供 CORS 头。因此,如果您在浏览器中或从外部域直接使用或链接这些资源,您可能会遇到问题。
为确保继续访问这些资产,您需要采取以下措施之一:
需要实际的代码示例? 如果您不确定如何开始或发现该过程难以实施,我们提供了实际的代码片段来帮助您入门。请参阅附录了解更多详细信息。
我的生成文件丢失了吗?
没有。 您可以随时从 API 检索新的 URL。
我没有直接返回您的 URL,但我进行了一些后处理。我应该担心吗?
如果您没有直接将我们的 URL 返回给您的用户,则可以跳过此更改。但是,我们建议您监控您的使用情况,特别是如果您发现系统出现任何错误高峰或异常行为。
如果您正在使用 FastAPI(易于移植或转换为任何其他框架或语言),下面是您可以如何调整现有代码以处理 CORS 并将文件本地保存:
当前代码:
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # API works
return resp.json()['url']
您可以轻松修改为:
import httpx
import uuid
import os # Import os module
from fastapi.responses import FileResponse
from fastapi import HTTPException
@app.post("/")
def generate(prompt: str) -> str:
resp = ... # same as above
url = resp.json()['url'] # Assuming 'url' is extracted from resp.json()
# Add the code below
file_id = str(uuid.uuid4())
# Ensure the directory exists
os.makedirs("./downloaded", exist_ok=True)
with httpx.Client() as client:
# Download the artifact
response = client.get(url)
# Check if the request was successful
if response.status_code == 200:
# Then resave it
with open(f"./downloaded/{file_id}.glb", "wb") as file:
file.write(response.content)
else:
raise HTTPException(status_code=response.status_code, detail="Failed to download artifact")
return file_id
@app.get("/artifact/{file_id}")
def download(file_id: str) -> FileResponse:
if os.path.exists(f"./downloaded/{file_id}.glb"):
return FileResponse(f"./downloaded/{file_id}.glb")
raise HTTPException(status_code=404, detail="Item not found")
下载并本地保存文件后,您应该使用新的 URL 为您的用户提供服务。例如,如果您的应用程序托管在 https://app.example.com,您应该提供服务的 URL 将如下所示:
https://app.example.com/artifact/<file_id>
此代码片段仅演示如何调整您的集成。但是,您应该考虑以下几点:
如果您正在开发单页应用程序 (SPA) 或前端后端 (BFF) 服务器,并且不希望直接在服务器上处理文件下载和存储,您可以使用边缘代理方法。这允许您从我们的服务器获取文件,应用必要的 CORS 头,然后将其提供给您的用户。
下面是使用 Cloudflare Workers 实现此方法的示例。此解决方案将通过下载文件并在您的域下重新发送来处理 CORS 问题。
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
// Parse the request URL
const url = new URL(request.url);
// Get the target URL from the 'url' query parameter
const targetUrl = url.searchParams.get('url');
// If no URL is provided, return an error
if (!targetUrl) {
return new Response('Please provide a URL parameter', {
status: 400,
headers: {
'Content-Type': 'text/plain',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type'
}
});
}
// Handle preflight OPTIONS request
if (request.method === 'OPTIONS') {
return handleCORS();
}
try {
// Fetch the file from the target URL
const response = await fetch(targetUrl);
// If the fetch failed, return the error
if (!response.ok) {
return new Response(`Failed to fetch from target URL: ${response.statusText}`, {
status: response.status,
headers: corsHeaders()
});
}
// Get the content type from the response or default to octet-stream
const contentType = response.headers.get('Content-Type') || 'application/octet-stream';
// Get the content disposition or create one from the URL
let contentDisposition = response.headers.get('Content-Disposition');
if (!contentDisposition) {
// Extract filename from the URL
const fileName = targetUrl.split('/').pop().split('?')[0] || 'file';
contentDisposition = `attachment; filename="${fileName}"`;
}
// Create a new response with CORS headers
const newResponse = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: {
'Content-Type': contentType,
'Content-Disposition': contentDisposition,
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Cache-Control': 'public, max-age=3600' // Cache for 1 hour
}
});
return newResponse;
} catch (error) {
return new Response(`Error fetching the file: ${error.message}`, {
status: 500,
headers: corsHeaders()
});
}
}
// Handle CORS preflight requests
function handleCORS() {
return new Response(null, {
status: 204, // No content
headers: corsHeaders()
});
}
// Create CORS headers object
function corsHeaders() {
return {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
'Access-Control-Max-Age': '86400' // 24 hours
};
}
一旦您的 Cloudflare Worker 设置完成,您就可以通过使用以下 JavaScript 代码来获取文件,将其集成到您的 SPA 中:
fetch('https://your-worker-url.workers.dev/?url=<target_url>')
.then(response => response.blob())
.then(blob => {
// Create a download link
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'downloaded-file.pdf';
document.body.appendChild(a);
a.click();
a.remove();
})
.catch(error => console.error('Error:', error));
此解决方案非常适用于需要绕过 CORS 限制,同时下载和向最终用户提供文件的 SPA 或 BFF 服务器。文件从目标 URL 获取,通过边缘代理,并与必要的 CORS 头一起提供。
如果您希望设置一个简单的 CORS 代理,您可以参考 Cloudflare 提供的此示例:Cloudflare Workers - CORS Header Proxy
这将允许您更轻松地处理跨域请求,同时仍然遵守 CORS 策略。
moving at the speed of creativity, achieving the depths of imagination.