關於如何使用生成檔案下載連結的重要更新

親愛的 Tripo 使用者,

我們寫信通知您一項即將到來的變更,這可能會影響您存取和使用我們平台資源的方式。

有哪些變更?

我們將對所有生成的 artifact URL 強制執行 CORS policy。這表示我們平台上託管的所有資源都需要對跨來源請求進行適當處理,換句話說,您不能直接使用 Tripo API 回傳給您的連結來提供生成的模型檔案。

為什麼要實施這項變更?

這項變更是我們致力於提升安全性防止未經授權存取不同來源資源的一部分。CORS(Cross-Origin Resource Sharing)是一種機制,允許伺服器指定哪些外部網域可以存取資源。
若想了解更多關於什麼是 CORS 的資訊,您可以參考 MDN 的指南

對您工作流程的影響

目前,我們不會為 artifact URL 提供 CORS headers。因此,如果您在瀏覽器中或從外部網域直接使用或連結到這些資源,可能會遇到問題。
為確保能持續存取這些 assets,您需要採取以下其中一項操作:

  1. 下載並在本機重新儲存 Artifacts:您可以下載 artifacts 並將其儲存在您的 backend。儲存在本機後,您就可以從自己的伺服器提供這些 assets,而不受 CORS 限制。
  2. 使用 Edge Proxy(例如 Next.js API Route、Cloudflare Workers):您可以使用 edge proxy 來擷取並提供 artifacts,同時在回應中加入必要的 CORS headers。例如:
    • Next.js API Route:設定一個 API route,從我們的平台擷取資源,並附上適當的 CORS headers 轉發。
    • Cloudflare Workers:使用 Cloudflare Workers 在從您的伺服器提供我們的 assets 時加入 CORS headers。

如何準備:

  • 檢視您的整合:確保您的系統能夠處理 artifact URL 的 CORS。
  • 下載並儲存 artifacts:如果您未使用 edge proxy,請考慮下載 artifacts 並將其儲存在您的 backend。
  • 設定 edge proxy:如果您使用 Next.js 或 Cloudflare Workers 等解決方案,請更新您的設定,以確保 assets 會以適當的 CORS headers 提供。
  • 測試您的系統:驗證所有對我們平台的跨來源請求都能如預期運作。

需要實用的程式碼範例嗎? 如果您不確定如何開始,或覺得實作流程困難,我們已提供實用的程式碼片段來協助您入門。請參閱附錄以取得更多詳細資訊。

FAQs

  • 我生成的檔案遺失了嗎?
    沒有。 您隨時都可以從 API 重新取得新的 URL。
  • 我不會直接回傳你們的 URL,但我會做一些後處理。我需要擔心嗎?
    如果您沒有直接將我們的 URL 回傳給您的使用者,您可以略過這項變更。不過,我們建議您監控使用情況,尤其是在您注意到系統中有任何錯誤激增或異常行為時。

需要協助?

我們了解這項變更可能需要您更新工作流程。如果您需要任何關於設定 edge proxy 的協助,或對這項轉換有任何疑問,請透過 support@tripo3d.ai 聯絡我們的支援團隊。
感謝您在我們提升平台安全性的過程中給予配合!祝您安全且高效!
此致,
Tripo 團隊

附錄 A:Backend 重新儲存方法

如果您使用 FastAPI(它很容易移植或轉換到任何其他 framework 或 language),以下說明如何調整現有程式碼,以處理 CORS 並將 artifact 儲存在本機:

目前程式碼:

python
@app.post("/")
def generate(prompt: str) -> str:
    resp = ...  # API works
    return resp.json()['url']

您可以輕鬆修改為:

python
import httpx
import uuid

from fastapi.responses import FileResponse
from fastapi import HTTPException

@app.post("/")
def generate(prompt: str) -> str:
    resp = ...  # same as above

    # Add the code below
    file_id = str(uuid.uuid4())
    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)

    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 會如下所示:

text
https://app.example.com/artifact/<file_id>

這段程式碼片段只是示範如何調整您的整合。不過,有幾項重要事項需要您考慮:

  • 加入適當的 authentication:確保下載連結受到適當 authentication methods 保護,以防止未經授權的存取。
  • 使用可靠的 storage:建議將檔案儲存到更可靠的 storage solutions,例如 dedicated servers 或像 S3 buckets 這類 cloud storage options。
  • 儲存 metadata:在您的 database 或其他 storage system 中保留 artifact metadata 的紀錄,以供未來使用(例如追蹤或稽核)。

附錄 B:Edge Proxy 方法

如果您正在開發 Single Page Application (SPA)Backend for Frontend (BFF) server,並且不想直接在自己的伺服器上處理檔案下載與儲存,您可以使用 edge proxy 方法。這可讓您從我們的伺服器擷取檔案、套用必要的 CORS headers,然後提供給您的使用者。

解決方案 1:使用 Edge Functions 下載並重新傳送檔案

以下是使用 Cloudflare Workers 實作此方法的範例。此解決方案會透過下載檔案並在您的網域下重新傳送,來處理 CORS 問題。

javascript
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 中:

javascript
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 servers。檔案會從 target URL 擷取,經由 edge proxy 傳遞,並附上必要的 CORS headers 提供。

解決方案 2:設定 CORS header proxy

如果您偏好設定一個簡單的 CORS proxy,可以參考 Cloudflare 提供的這個範例:Cloudflare Workers - CORS Header Proxy
這將讓您更輕鬆地處理跨來源請求,同時仍符合 CORS policies。

重要考量:

  • Authentication:請務必使用 authentication methods(例如 API keys、OAuth 等)妥善保護您的 edge functions,以防止未經授權存取您的檔案。
  • Storage:雖然這種方法可行,但我們仍建議您將 artifact 儲存在您的伺服器上以供未來使用,尤其是在您需要管理大規模下載或確保可靠存取資源時。
分享文章

用 3D 生成萬物

點擊下方,加入數百萬 3D 創作者的行列。體驗超高保真模型生成與一流的 PBR 貼圖。