Downloading Original File Instead of .SVF from Autodesk Viewer

I'm trying to allow users to download models directly from the Autodesk Viewer in my web application. I followed this Autodesk blog post and successfully implemented the functionality using signed cookies.

The issue I'm facing is that the downloaded file is in .svf format. I want to download the original file in its original format (e.g., .dwg, .rvt, .obj, etc.). My backend is Node.js/express with AWS and my frontend is in React. I know the models get translated and that's why the file format is .svf but I really need to download the original file.

Is there any way I can retrieve and download the original file (in its native format) instead of the .svf derivative? If so, how can I modify my implementation to achieve this? As a last resort, I could store the original versions of the models in S3 and let users download them from there - but I think that is a lot of wasted space which I would like to avoid if possible.

Also (this might be a wholly different topic) - is there any way to delete files from the viewer dropdown? If a user wants to delete a file from the browser, can they do it?

I was using the code from this tutorial - https://get-started.aps.autodesk.com/tutorials/simple-viewer/

Any guidance would be greatly appreciated!

I set up an API route to generate a signed URL for the derivative file:

router.get("/api/model-download-url", async (req, res) => {
  const { urn } = req.query;

  if (!urn) {
    return res.status(400).json({ error: "Missing URN" });
  }

  const { access_token } = await getInternalToken();

  const manifest = await getManifest(urn);

  const derivativeUrn =
    manifest?.derivatives?.[0]?.children?.[0]?.children?.find(
      (c) => c.role === "graphics"
    )?.urn;

  try {
    const response = await fetch(
      `https://developer.api.autodesk.com/modelderivative/v2/designdata/${urn}/manifest/${derivativeUrn}/signedcookies`,
      {
        method: "GET",
        headers: {
          Authorization: `Bearer ${access_token}`,
        },
      }
    );

    const data = await response.json();
    const url = data.url;

    const setCookieHeaders = response.headers.getSetCookie();
    if (!setCookieHeaders || setCookieHeaders.length === 0) {
      console.error("No Set-Cookie headers found");
      return res.status(500).json({ error: "No Set-Cookie headers found" });
    }

    let signedParams = {};

    setCookieHeaders.forEach((cookie) => {
      const parts = cookie.split(";")[0].split("=");
      if (parts[0].startsWith("CloudFront-")) {
        signedParams[parts[0].replace("CloudFront-", "")] = parts[1];
      }
    });

    if (
      !signedParams["Key-Pair-Id"] ||
      !signedParams["Policy"] ||
      !signedParams["Signature"]
    ) {
      return res.status(500).json({ error: "Missing signed query parameters" });
    }

    const signedUrl = `${url}?Key-Pair-Id=${signedParams["Key-Pair-Id"]}&Policy=${signedParams["Policy"]}&Signature=${signedParams["Signature"]}`;

    res.json({ signedUrl });
  } catch (error) {
    console.error("Error fetching signed cookies:", error);
    res.status(500).json({

This is how I'm calling it in the frontend:

const handleDownload = async () => {
  if (!viewer) return;

  const urn = viewer.model.getData().urn;

  try {
    const response = await fetch(
      `/api/model-download-url?urn=${encodeURIComponent(urn)}`
    );

    const data = await response.json();

    if (!data.signedUrl) {
      throw new Error("Failed to retrieve signed URL.");
    }

    // Open file in new tab
    window.open(data.signedUrl, "_blank");
  } catch (error) {
    console.error("Error downloading model:", error);
  }
};

Upvotes: -1

Views: 29

Answers (1)

Eason Kang
Eason Kang

Reputation: 7070

The original model file (i.e., Source model file) used to be translated to SVF/SVF2 doesn't exist in the Model Derivative service. Instead, it's stored on the OSS bucket.

If you follow the simple viewer tutorial, then you can find the source file in the bucket named with ${APS_CLIENT_ID.toLowerCase()}-basic-app where the APS_CLIENT_ID is your APS Client ID. Here is how the sample code creates a bucket for you:

Note. if you override the APS_BUCKET value in env variable, then you must fine it there instead.

service.ensureBucketExists = async (bucketKey) => {
    const accessToken = await getInternalToken();
    try {
        await ossClient.getBucketDetails(bucketKey, { accessToken });
    } catch (err) {
        if (err.axiosError.response.status === 404) {
            await ossClient.createBucket(Region.Us, { bucketKey: bucketKey, policyKey: PolicyKey.Persistent }, { accessToken});
        } else {
            throw err;  
        }
    }
};

service.listObjects = async () => {
    await service.ensureBucketExists(APS_BUCKET);
    const accessToken = await getInternalToken();
    let resp = await ossClient.getObjects(APS_BUCKET, { limit: 64, accessToken });
    let objects = resp.items;
    while (resp.next) {
        const startAt = new URL(resp.next).searchParams.get('startAt');
        resp = await ossClient.getObjects(APS_BUCKET, { limit: 64, startAt, accessToken });
        objects = objects.concat(resp.items);
    }
    return objects;
};

Now back to your inquiry, to download the original model file (i.e., Source model file), we need to use GET buckets/:bucketKey/objects/:objectKey/signeds3download of the OSS (Object Storage Service) API, and require two parameters: bucket key and object key.

We can get that by decoding the URN you passed to Model Derivative API.

For example,

The URN is dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6bXlidWNrZXQvcmFjX3NhbXBsZV9ob3VzZS5ydnQ

After base64 decoded it, we get urn:adsk.objects:os.object:mybucket/rac_sample_house.rvt,

So bucket key is mybucket and object key is rac_sample_house.rvt.

Then call GET buckets/:bucketKey/objects/:objectKey/signeds3download to get the download URL:

curl --location 'https://developer.api.autodesk.com/oss/v2/buckets/mybucket/objects/rac_sample_house.rvt/signeds3download' \
--header 'Authorization: Bearer ...'

Here is a code sample in node.js:

const ossClient = new OssClient();
const accessToken = await getInternalToken();

let decodedUrn = Buffer.from(urn, 'base64').toString('utf8');
let urnParts = decodedUrn.replace('urn:adsk.objects:os.object:', '').split('/');

let bucketKey = urnParts[0];
let objectKey = urnParts[1];


let response = await ossClient.signedS3Download(bucketKey, objectKey, { accessToken });


// Download the file by using `response.url`

ref:

To delete files from the viewer dropdown, we can do the following in backend (the routers of express.js):

// delete translated SVF/SVF2 file for the selected file from Model Derivative service
await modelDerivativeClient.deleteManifest(urn, { accessToken });

// Delete the source model file from OSS
await ossClient.deleteObject(bucketKey, objectKey, { accessToken });

Upvotes: 0

Related Questions