如何在Node.js中通过Autodesk APS API下载SVF格式模型衍生品
解决方案
修正后的代码实现
const fs = require('fs').promises; const path = require('path'); const { AuthClientTwoLegged, DerivativesApi } = require('@aps/node'); const crypto = require('crypto'); const CLIENT_ID = process.env.APS_CLIENT_ID; const CLIENT_SECRET = process.env.APS_CLIENT_SECRET; const OBJECT_URN = 'your-object-urn'; // Base64编码的URN const OUTPUT_DIR = './downloads'; // 递归遍历所有衍生文件节点 async function traverseDerivativeNodes(node, derivativesApi, token, baseDir) { // 处理当前节点的文件(如果有urn) if (node.urn) { // 解码URN得到实际文件路径(APS返回的urn是Base64编码的相对路径) const decodedUrn = Buffer.from(node.urn, 'base64').toString('utf8'); const filePath = path.join(baseDir, decodedUrn); const fileDir = path.dirname(filePath); // 创建目录(如果不存在) await fs.mkdir(fileDir, { recursive: true }); console.log(`Downloading: ${decodedUrn} -> ${filePath}`); // 以流的方式下载,避免内存溢出(适合大文件) const response = await derivativesApi.getDerivativeManifest(OBJECT_URN, node.urn, {}, { authorization: `Bearer ${token.access_token}`, responseType: 'stream' }); // 写入文件流 const writeStream = fs.createWriteStream(filePath); await new Promise((resolve, reject) => { response.body.pipe(writeStream); writeStream.on('finish', resolve); writeStream.on('error', reject); }); // 可选:验证文件哈希完整性 if (node.hashed) { const fileBuffer = await fs.readFile(filePath); const hash = crypto.createHash('md5').update(fileBuffer).digest('hex'); if (hash !== node.hashed.toLowerCase()) { throw new Error(`File integrity check failed: ${decodedUrn}`); } console.log(`Integrity check passed: ${decodedUrn}`); } } // 递归处理子节点 if (node.children && Array.isArray(node.children)) { for (const child of node.children) { await traverseDerivativeNodes(child, derivativesApi, token, baseDir); } } } async function downloadSVF() { // 初始化认证客户端 const authClient = new AuthClientTwoLegged(CLIENT_ID, CLIENT_SECRET, ['data:read'], true); const token = await authClient.authenticate(); const derivativesApi = new DerivativesApi(); // 创建输出根目录 await fs.mkdir(OUTPUT_DIR, { recursive: true }); // 获取模型清单 const manifest = await derivativesApi.getManifest(OBJECT_URN, {}, { authorization: `Bearer ${token.access_token}` }); const derivatives = manifest.body.derivatives; // 遍历所有SVF类型的衍生品 for (const derivative of derivatives) { if (derivative.outputType === 'svf') { // 递归遍历该衍生品下的所有文件节点 await traverseDerivativeNodes(derivative, derivativesApi, token, OUTPUT_DIR); } } console.log('All SVF derivatives downloaded successfully!'); } downloadSVF().catch(console.error);
针对疑问的解答
1. 确保下载所有相关文件
- 递归遍历节点:SVF衍生品的文件结构是嵌套的(比如.sdb、纹理文件可能在子节点的子节点中),需要递归遍历
children层级,而非仅遍历直接子节点。 - 解码URN路径:API返回的
node.urn是Base64编码的相对路径,解码后才能还原原始的目录结构(比如models/0.sdb),避免把完整路径当成单个文件名。 - 创建目录结构:根据解码后的路径自动创建对应的子目录,确保文件保存到正确的位置。
2. 复现VS Code扩展的下载功能
VS Code的APS扩展没有使用特殊API端点,核心工作流和上述代码一致:
- 通过
getManifest获取模型的完整衍生清单 - 递归遍历清单中所有
outputType为svf的节点及其子节点 - 对每个节点,调用
getDerivativeManifest下载文件,并按原始目录结构保存
3. 大型文件与完整性最佳实践
- 流式下载:设置
responseType: 'stream',用流的方式写入文件,避免将大文件全部加载到内存中导致溢出。 - 文件哈希验证:每个节点的
hashed字段是文件的MD5哈希值,下载后计算本地文件的MD5并对比,确保文件未损坏。 - 错误重试机制:可以为下载逻辑添加重试逻辑(比如使用
p-retry库),处理临时网络错误。 - 断点续传:对于超大文件,可使用HTTP的
Range请求头,只下载未完成的部分(需要记录已下载的字节数)。
内容的提问来源于stack exchange,提问作者Lokeswar Mamallan
相关产品推荐
相关产品推荐

