tripo logo
allPosts

Important Update Regarding How to Use Generated File Download Links

Dear Tripo Users,

We are writing to inform you of an upcoming change that may affect how you access and use resources from our platform.

What’s Changing?

We will be enforcing CORS policy on all generated artifact URLs. This means that all resources hosted on our platform will require appropriate handling for cross-origin requests, or in other words, you cannot directly serving generated model files using the link Tripo's API returns to you.

Why Is This Being Implemented?

This change is part of our commitment to improving security and preventing unauthorized access to resources across different origins. CORS (Cross-Origin Resource Sharing) is a mechanism that allows servers to specify which external domains can access resources.


For more information about what is CORS, you can refer to the MDN's guide.

Impact on Your Workflow

Currently, we do not provide CORS headers for artifact URLs. As a result, if you are using or linking to these resources directly in a browser or from external domains, you may experience issues.


To ensure continued access to these assets, you will need to take one of the following actions:

  1. Download and Resave Artifacts Locally: You may download the artifacts and store them on your backend. Once saved locally, you can serve these assets from your server without CORS restrictions.
  2. Use an Edge Proxy (e.g., Next.js API Route, Cloudflare Workers): You can use an edge proxy to fetch and serve the artifacts while adding the necessary CORS headers to the responses. For instance:
    • Next.js API Route: Set up an API route to fetch resources from our platform and forward them with the appropriate CORS headers.
    • Cloudflare Workers: Use Cloudflare Workers to add CORS headers when serving our assets from your server.

How to Prepare:

  • Review your integration: Ensure that your system is capable of handling CORS for artifact URLs.
  • Download and save artifacts: If you're not using an edge proxy, consider downloading and storing the artifacts on your backend.
  • Set up an edge proxy: If you're using solutions like Next.js or Cloudflare Workers, update your configuration to ensure that assets are served with appropriate CORS headers.
  • Test your system: Verify that all cross-origin requests to our platform work as expected.

Need practical code examples? If you're unsure how to start or find the process hard to implement, we’ve provided practical code snippets to help you get started. Please refer to the Appendix for more details.

FAQs

  • Is my generated file lost?

    No. You can always retrieve a new URL from the API at any time.

  • I don't return your URL directly, but I do some post-processing. Should I be concerned?

    If you're not returning our URL directly to your users, you can skip this change. However, we recommend monitoring your usage, especially if you notice any error spikes or abnormal behavior in your system.

Need Assistance?

We understand that this change may require updates to your workflow. If you need any help setting up an edge proxy or have any questions about this transition, please reach out to our support team at support@tripo3d.ai.


We appreciate your cooperation as we work to enhance security on our platform! Stay safe and productive!


Best regards,


The Tripo Team



Appendix A: Backend resave approach

If you're using FastAPI (which is easy to port or translate to any other framework or language), here's how you can adjust your existing code to handle CORS and save the artifact locally:

Current Code:

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

You can easily modify it to:

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")

After downloading and saving the file locally, you should serve your users with the new URL. For example, if your application is hosted at https://app.example.com, the URL you should be serving will look like this:

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

This code snippet is just a demonstration of how to adjust your integration. However, there are a few important things you should consider:

  • Add proper authentication: Ensure that the download links are protected by the appropriate authentication methods to prevent unauthorized access.
  • Use reliable storage: It’s recommended to save the files to more reliable storage solutions, such as dedicated servers or cloud storage options like S3 buckets.
  • Store metadata: Keep a record of the artifact metadata in your database or another storage system for future use (such as tracking or auditing).

Appendix B: Edge Proxy Approach

If you're developing a Single Page Application (SPA) or a Backend for Frontend (BFF) server and prefer not to handle file downloads and storage directly on your server, you can use an edge proxy approach. This allows you to fetch the file from our server, apply the necessary CORS headers, and then serve it to your users.

Solution 1: Download and Resend the File Using Edge Functions

Below is an example of how to implement this approach using Cloudflare Workers. This solution will handle the CORS issues by downloading the file and resending it under your domain.

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
  };
}

Once your Cloudflare Worker is set up, you can integrate it into your SPA by using the following JavaScript code to fetch the file:

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));

This solution is perfect for SPAs or BFF servers that need to bypass CORS restrictions while downloading and serving files to end users. The file is fetched from the target URL, passed through the edge proxy, and served with the necessary CORS headers.

Solution 2: Setup a CORS header proxy

If you prefer to set up a simple CORS proxy, you can refer to this example provided by Cloudflare: Cloudflare Workers - CORS Header Proxy


This will allow you to handle cross-origin requests more easily, while still complying with CORS policies.

Important Considerations:

  • Authentication: Make sure to properly secure your edge functions with authentication methods (e.g., API keys, OAuth, etc.) to prevent unauthorized access to your files.
  • Storage: While this approach works, we still recommend saving the artifact on your server for future use, especially if you need to manage large-scale downloads or ensure reliable access to resources.