Autodesk forge 如何使用衍生工具脱机使用forge viewer

Autodesk forge 如何使用衍生工具脱机使用forge viewer,autodesk-forge,autodesk-model-derivative,Autodesk Forge,Autodesk Model Derivative,参考这篇文章,[,我知道派生api提供的文件“GET:urn/manifest/:派生EURN”对于脱机查看来说已经足够了。但是在脱机查看器中使用与输入相同的内容不起作用。在上分析现有示例文件的演示表明下载的bubble还有其他文件,如bin/pack/zip/json,使用C#Api从何处获取这些文件?上载示例文件并使用模型提取器返回错误(“无法获取/extract/1906495 seatdwf.zip”)[根据建议,已尝试3月24日的extract.autodesk.io版本,但无效。]

参考这篇文章,[,我知道派生api提供的文件“GET:urn/manifest/:派生EURN”对于脱机查看来说已经足够了。但是在脱机查看器中使用与输入相同的内容不起作用。在上分析现有示例文件的演示表明下载的bubble还有其他文件,如bin/pack/zip/json,使用C#Api从何处获取这些文件?上载示例文件并使用模型提取器返回错误(“无法获取/extract/1906495 seatdwf.zip”)[根据建议,已尝试3月24日的extract.autodesk.io版本,但无效。] 请指导如何使用C#下载脱机查看所需的文件
提前感谢。

以下是我从extract.autodesk.io中编写的提取器的ES6+异步版本中的Node.js版本:

import BaseSvc from './BaseSvc'
import archiver from 'archiver'
import Forge from 'forge-apis'
import request from 'request'
import mkdirp from 'mkdirp'
import Zip from 'node-zip'
import Zlib from 'zlib'
import path from 'path'
import _ from 'lodash'
import fs from 'fs'

export default class ExtractorSvc extends BaseSvc {

  /////////////////////////////////////////////////////////
  //
  //
  /////////////////////////////////////////////////////////
  constructor (config) {

    super (config)

    this.derivativesAPI = new Forge.DerivativesApi()
  }

  /////////////////////////////////////////////////////////
  //
  //
  /////////////////////////////////////////////////////////
  name () {

    return 'ExtractorSvc'
  }

  /////////////////////////////////////////////////////////
  // Create directory async
  //
  /////////////////////////////////////////////////////////
  mkdirpAsync (dir) {

    return new Promise((resolve, reject) => {
      mkdirp(dir, (error) => {
        return error
          ? reject (error)
          : resolve()
      })
    })
  }

  /////////////////////////////////////////////////////////
  // download all URN resources to target directory
  // (unzipped)
  //
  /////////////////////////////////////////////////////////
  download (getToken, urn, directory) {

    return new Promise (async (resolve, reject) => {

      // make sure target dir exists
    await this.mkdirpAsync (directory)

      // get token, can be object token or an async
      // function that returns the token
      const token = ((typeof getToken == 'function')
        ? await getToken()
    : getToken)

      // get URN top level manifest
      const manifest =
        await this.derivativesAPI.getManifest (
        urn, {}, {autoRefresh:false}, token)

      // harvest derivatives
      const derivatives = await this.getDerivatives (
        getToken, manifest.body)

      // format derivative resources
      const nestedDerivatives = derivatives.map((item) => {

        return item.files.map((file) => {

          const localPath = path.resolve(
            directory, item.localPath)

          return {
            basePath: item.basePath,
            guid: item.guid,
            mime: item.mime,
            fileName: file,
            urn: item.urn,
            localPath
          }
        })
      })

      // flatten resources
      const derivativesList = _.flattenDeep(
        nestedDerivatives)

      // creates async download tasks for each
      // derivative file
      const downloadTasks = derivativesList.map(
        (derivative) => {

          return new Promise(async(resolve) => {

            const urn = path.join(
              derivative.basePath,
              derivative.fileName)

            const data = await this.getDerivative(
              getToken, urn)

            const filename = path.resolve(
              derivative.localPath,
              derivative.fileName)

          await this.saveToDisk(data, filename)

            resolve(filename)
          })
        })

      // wait for all files to be downloaded
      const files = await Promise.all(downloadTasks)

      resolve(files)
    })
  }

  /////////////////////////////////////////////////////////
  // Parse top level manifest to collect derivatives
  //
  /////////////////////////////////////////////////////////
  parseManifest (manifest) {

    const items = []

    const parseNodeRec = (node) => {

      const roles = [
        'Autodesk.CloudPlatform.DesignDescription',
        'Autodesk.CloudPlatform.PropertyDatabase',
        'Autodesk.CloudPlatform.IndexableContent',
        'leaflet-zip',
        'thumbnail',
        'graphics',
        'preview',
        'raas',
        'pdf',
        'lod',
      ]

      if (roles.includes(node.role)) {

        const item = {
          guid: node.guid,
          mime: node.mime
        }

        const pathInfo = this.getPathInfo(node.urn)

        items.push (Object.assign({}, item, pathInfo))
      }

      if (node.children) {

        node.children.forEach ((child) => {

          parseNodeRec (child)
        })
      }
    }

    parseNodeRec({
      children: manifest.derivatives
    })

    return items
  }

  /////////////////////////////////////////////////////////
  // Collect derivatives for SVF
  //
  /////////////////////////////////////////////////////////
  getSVFDerivatives (getToken, item) {

    return new Promise(async(resolve, reject) => {

      try {

        const svfPath = item.urn.slice (
          item.basePath.length)

        const files = [svfPath]

        const data = await this.getDerivative (
          getToken, item.urn)

        const pack = new Zip (data, {
          checkCRC32: true,
          base64: false
        })

        const manifestData =
          pack.files['manifest.json'].asNodeBuffer()

        const manifest = JSON.parse (
          manifestData.toString('utf8'))

        if (manifest.assets) {

          manifest.assets.forEach((asset) => {

            // Skip SVF embedded resources
            if (asset.URI.indexOf('embed:/') === 0) {
              return
            }

            files.push(asset.URI)
          })
        }

        return resolve(
          Object.assign({}, item, {
            files
          }))

      } catch (ex) {

        reject (ex)
      }
    })
  }

  /////////////////////////////////////////////////////////
  // Collect derivatives for F2D
  //
  /////////////////////////////////////////////////////////
  getF2dDerivatives (getToken, item) {

    return new Promise(async(resolve, reject) => {

      try {

        const files = ['manifest.json.gz']

        const manifestPath = item.basePath +
          'manifest.json.gz'

        const data = await this.getDerivative (
          getToken, manifestPath)

        const manifestData = Zlib.gunzipSync(data)

        const manifest = JSON.parse (
          manifestData.toString('utf8'))

        if (manifest.assets) {

          manifest.assets.forEach((asset) => {

            // Skip SVF embedded resources
            if (asset.URI.indexOf('embed:/') === 0) {
              return
            }

            files.push(asset.URI)
          })
        }

        return resolve(
          Object.assign({}, item, {
            files
          }))

      } catch (ex) {

        reject (ex)
      }
    })
  }

  /////////////////////////////////////////////////////////
  // Get all derivatives from top level manifest
  //
  /////////////////////////////////////////////////////////
  getDerivatives (getToken, manifest) {

    return new Promise(async(resolve, reject) => {

      const items = this.parseManifest(manifest)

      const derivativeTasks = items.map((item) => {

        switch (item.mime) {

          case 'application/autodesk-svf':
            return this.getSVFDerivatives(
              getToken, item)

          case 'application/autodesk-f2d':
            return this.getF2dDerivatives(
              getToken, item)

          case 'application/autodesk-db':
            return Promise.resolve(
              Object.assign({}, item, {
                files: [
                  'objects_attrs.json.gz',
                  'objects_vals.json.gz',
                  'objects_offs.json.gz',
                  'objects_ids.json.gz',
                  'objects_avs.json.gz',
                  item.rootFileName
                ]}))

          default:
            return Promise.resolve(
              Object.assign({}, item, {
                files: [
                  item.rootFileName
                ]}))
        }
      })

      const derivatives = await Promise.all(
        derivativeTasks)

      return resolve(derivatives)
    })
  }

  /////////////////////////////////////////////////////////
  // Generate path information from URN
  //
  /////////////////////////////////////////////////////////
  getPathInfo (encodedURN) {

    const urn = decodeURIComponent(encodedURN)

    const rootFileName = urn.slice (
      urn.lastIndexOf ('/') + 1)

    const basePath = urn.slice (
      0, urn.lastIndexOf ('/') + 1)

    const localPathTmp = basePath.slice (
      basePath.indexOf ('/') + 1)

    const localPath = localPathTmp.replace (
      /^output\//, '')

    return {
      rootFileName,
      localPath,
      basePath,
      urn
    }
  }

  /////////////////////////////////////////////////////////
  // Get derivative data for specific URN
  //
  /////////////////////////////////////////////////////////
  getDerivative (getToken, urn) {

    return new Promise(async(resolve, reject) => {

      const baseUrl = 'https://developer.api.autodesk.com/'

      const url = baseUrl +
        `derivativeservice/v2/derivatives/${urn}`

      const token = ((typeof getToken == 'function')
        ? await getToken()
    : getToken)

      request({
        url,
        method: 'GET',
        headers: {
          'Authorization': 'Bearer ' + token.access_token,
          'Accept-Encoding': 'gzip, deflate'
        },
        encoding: null
      }, (err, response, body) => {

        if (err) {

          return reject(err)
        }

        if (body && body.errors) {

          return reject(body.errors)
        }

        if ([200, 201, 202].indexOf(
            response.statusCode) < 0) {

          return reject(response)
        }

        return resolve(body || {})
      })
    })
  }

  /////////////////////////////////////////////////////////
  // Save data to disk
  //
  /////////////////////////////////////////////////////////
  saveToDisk (data, filename) {

    return new Promise(async(resolve, reject) => {

    await this.mkdirpAsync(path.dirname(filename))

      const wstream = fs.createWriteStream(filename)

      const ext = path.extname(filename)

      wstream.on('finish', () => {

        resolve()
      })

      if (typeof data === 'object' && ext === '.json') {

        wstream.write(JSON.stringify(data))

      } else {

        wstream.write(data)
      }

      wstream.end()
    })
  }

  /////////////////////////////////////////////////////////
  // Create a zip
  //
  /////////////////////////////////////////////////////////
  createZip (rootDir, zipfile, zipRoot, files) {

    return new Promise((resolve, reject) => {

      try {

        const output = fs.createWriteStream(zipfile)

        const archive = archiver('zip')

        output.on('close', () => {

          resolve()
        })

        archive.on('error', (err) => {

          reject(err)
        })

        archive.pipe(output)

        if (files) {

          files.forEach((file) => {

            try {

              const rs = fs.createReadStream(file)

              archive.append(rs, {
                name:
                  `${zipRoot}/${file.replace(rootDir, '')}`
              })

            } catch(ex){

              console.log(ex)
            }
          })

        } else {

          archive.bulk([ {
            expand: false,
            src: [rootDir + '/*']
          }])
        }

        archive.finalize()

      } catch (ex) {

        reject(ex)
      }
    })
  }
}

以下是我从extract.autodesk.io编写的提取器的ES6+async中的一个清晰注释Node.js版本:

import BaseSvc from './BaseSvc'
import archiver from 'archiver'
import Forge from 'forge-apis'
import request from 'request'
import mkdirp from 'mkdirp'
import Zip from 'node-zip'
import Zlib from 'zlib'
import path from 'path'
import _ from 'lodash'
import fs from 'fs'

export default class ExtractorSvc extends BaseSvc {

  /////////////////////////////////////////////////////////
  //
  //
  /////////////////////////////////////////////////////////
  constructor (config) {

    super (config)

    this.derivativesAPI = new Forge.DerivativesApi()
  }

  /////////////////////////////////////////////////////////
  //
  //
  /////////////////////////////////////////////////////////
  name () {

    return 'ExtractorSvc'
  }

  /////////////////////////////////////////////////////////
  // Create directory async
  //
  /////////////////////////////////////////////////////////
  mkdirpAsync (dir) {

    return new Promise((resolve, reject) => {
      mkdirp(dir, (error) => {
        return error
          ? reject (error)
          : resolve()
      })
    })
  }

  /////////////////////////////////////////////////////////
  // download all URN resources to target directory
  // (unzipped)
  //
  /////////////////////////////////////////////////////////
  download (getToken, urn, directory) {

    return new Promise (async (resolve, reject) => {

      // make sure target dir exists
    await this.mkdirpAsync (directory)

      // get token, can be object token or an async
      // function that returns the token
      const token = ((typeof getToken == 'function')
        ? await getToken()
    : getToken)

      // get URN top level manifest
      const manifest =
        await this.derivativesAPI.getManifest (
        urn, {}, {autoRefresh:false}, token)

      // harvest derivatives
      const derivatives = await this.getDerivatives (
        getToken, manifest.body)

      // format derivative resources
      const nestedDerivatives = derivatives.map((item) => {

        return item.files.map((file) => {

          const localPath = path.resolve(
            directory, item.localPath)

          return {
            basePath: item.basePath,
            guid: item.guid,
            mime: item.mime,
            fileName: file,
            urn: item.urn,
            localPath
          }
        })
      })

      // flatten resources
      const derivativesList = _.flattenDeep(
        nestedDerivatives)

      // creates async download tasks for each
      // derivative file
      const downloadTasks = derivativesList.map(
        (derivative) => {

          return new Promise(async(resolve) => {

            const urn = path.join(
              derivative.basePath,
              derivative.fileName)

            const data = await this.getDerivative(
              getToken, urn)

            const filename = path.resolve(
              derivative.localPath,
              derivative.fileName)

          await this.saveToDisk(data, filename)

            resolve(filename)
          })
        })

      // wait for all files to be downloaded
      const files = await Promise.all(downloadTasks)

      resolve(files)
    })
  }

  /////////////////////////////////////////////////////////
  // Parse top level manifest to collect derivatives
  //
  /////////////////////////////////////////////////////////
  parseManifest (manifest) {

    const items = []

    const parseNodeRec = (node) => {

      const roles = [
        'Autodesk.CloudPlatform.DesignDescription',
        'Autodesk.CloudPlatform.PropertyDatabase',
        'Autodesk.CloudPlatform.IndexableContent',
        'leaflet-zip',
        'thumbnail',
        'graphics',
        'preview',
        'raas',
        'pdf',
        'lod',
      ]

      if (roles.includes(node.role)) {

        const item = {
          guid: node.guid,
          mime: node.mime
        }

        const pathInfo = this.getPathInfo(node.urn)

        items.push (Object.assign({}, item, pathInfo))
      }

      if (node.children) {

        node.children.forEach ((child) => {

          parseNodeRec (child)
        })
      }
    }

    parseNodeRec({
      children: manifest.derivatives
    })

    return items
  }

  /////////////////////////////////////////////////////////
  // Collect derivatives for SVF
  //
  /////////////////////////////////////////////////////////
  getSVFDerivatives (getToken, item) {

    return new Promise(async(resolve, reject) => {

      try {

        const svfPath = item.urn.slice (
          item.basePath.length)

        const files = [svfPath]

        const data = await this.getDerivative (
          getToken, item.urn)

        const pack = new Zip (data, {
          checkCRC32: true,
          base64: false
        })

        const manifestData =
          pack.files['manifest.json'].asNodeBuffer()

        const manifest = JSON.parse (
          manifestData.toString('utf8'))

        if (manifest.assets) {

          manifest.assets.forEach((asset) => {

            // Skip SVF embedded resources
            if (asset.URI.indexOf('embed:/') === 0) {
              return
            }

            files.push(asset.URI)
          })
        }

        return resolve(
          Object.assign({}, item, {
            files
          }))

      } catch (ex) {

        reject (ex)
      }
    })
  }

  /////////////////////////////////////////////////////////
  // Collect derivatives for F2D
  //
  /////////////////////////////////////////////////////////
  getF2dDerivatives (getToken, item) {

    return new Promise(async(resolve, reject) => {

      try {

        const files = ['manifest.json.gz']

        const manifestPath = item.basePath +
          'manifest.json.gz'

        const data = await this.getDerivative (
          getToken, manifestPath)

        const manifestData = Zlib.gunzipSync(data)

        const manifest = JSON.parse (
          manifestData.toString('utf8'))

        if (manifest.assets) {

          manifest.assets.forEach((asset) => {

            // Skip SVF embedded resources
            if (asset.URI.indexOf('embed:/') === 0) {
              return
            }

            files.push(asset.URI)
          })
        }

        return resolve(
          Object.assign({}, item, {
            files
          }))

      } catch (ex) {

        reject (ex)
      }
    })
  }

  /////////////////////////////////////////////////////////
  // Get all derivatives from top level manifest
  //
  /////////////////////////////////////////////////////////
  getDerivatives (getToken, manifest) {

    return new Promise(async(resolve, reject) => {

      const items = this.parseManifest(manifest)

      const derivativeTasks = items.map((item) => {

        switch (item.mime) {

          case 'application/autodesk-svf':
            return this.getSVFDerivatives(
              getToken, item)

          case 'application/autodesk-f2d':
            return this.getF2dDerivatives(
              getToken, item)

          case 'application/autodesk-db':
            return Promise.resolve(
              Object.assign({}, item, {
                files: [
                  'objects_attrs.json.gz',
                  'objects_vals.json.gz',
                  'objects_offs.json.gz',
                  'objects_ids.json.gz',
                  'objects_avs.json.gz',
                  item.rootFileName
                ]}))

          default:
            return Promise.resolve(
              Object.assign({}, item, {
                files: [
                  item.rootFileName
                ]}))
        }
      })

      const derivatives = await Promise.all(
        derivativeTasks)

      return resolve(derivatives)
    })
  }

  /////////////////////////////////////////////////////////
  // Generate path information from URN
  //
  /////////////////////////////////////////////////////////
  getPathInfo (encodedURN) {

    const urn = decodeURIComponent(encodedURN)

    const rootFileName = urn.slice (
      urn.lastIndexOf ('/') + 1)

    const basePath = urn.slice (
      0, urn.lastIndexOf ('/') + 1)

    const localPathTmp = basePath.slice (
      basePath.indexOf ('/') + 1)

    const localPath = localPathTmp.replace (
      /^output\//, '')

    return {
      rootFileName,
      localPath,
      basePath,
      urn
    }
  }

  /////////////////////////////////////////////////////////
  // Get derivative data for specific URN
  //
  /////////////////////////////////////////////////////////
  getDerivative (getToken, urn) {

    return new Promise(async(resolve, reject) => {

      const baseUrl = 'https://developer.api.autodesk.com/'

      const url = baseUrl +
        `derivativeservice/v2/derivatives/${urn}`

      const token = ((typeof getToken == 'function')
        ? await getToken()
    : getToken)

      request({
        url,
        method: 'GET',
        headers: {
          'Authorization': 'Bearer ' + token.access_token,
          'Accept-Encoding': 'gzip, deflate'
        },
        encoding: null
      }, (err, response, body) => {

        if (err) {

          return reject(err)
        }

        if (body && body.errors) {

          return reject(body.errors)
        }

        if ([200, 201, 202].indexOf(
            response.statusCode) < 0) {

          return reject(response)
        }

        return resolve(body || {})
      })
    })
  }

  /////////////////////////////////////////////////////////
  // Save data to disk
  //
  /////////////////////////////////////////////////////////
  saveToDisk (data, filename) {

    return new Promise(async(resolve, reject) => {

    await this.mkdirpAsync(path.dirname(filename))

      const wstream = fs.createWriteStream(filename)

      const ext = path.extname(filename)

      wstream.on('finish', () => {

        resolve()
      })

      if (typeof data === 'object' && ext === '.json') {

        wstream.write(JSON.stringify(data))

      } else {

        wstream.write(data)
      }

      wstream.end()
    })
  }

  /////////////////////////////////////////////////////////
  // Create a zip
  //
  /////////////////////////////////////////////////////////
  createZip (rootDir, zipfile, zipRoot, files) {

    return new Promise((resolve, reject) => {

      try {

        const output = fs.createWriteStream(zipfile)

        const archive = archiver('zip')

        output.on('close', () => {

          resolve()
        })

        archive.on('error', (err) => {

          reject(err)
        })

        archive.pipe(output)

        if (files) {

          files.forEach((file) => {

            try {

              const rs = fs.createReadStream(file)

              archive.append(rs, {
                name:
                  `${zipRoot}/${file.replace(rootDir, '')}`
              })

            } catch(ex){

              console.log(ex)
            }
          })

        } else {

          archive.bulk([ {
            expand: false,
            src: [rootDir + '/*']
          }])
        }

        archive.finalize()

      } catch (ex) {

        reject(ex)
      }
    })
  }
}

extract.autodesk.io问题在几分钟前已修复,您是否可以重试并确认可以下载?或者将导致问题的模型指向我,thx@PhilippeLeefsma,感谢您的及时回复。我可以从extract.autodesk.io live demo的更新版本下载bubbles。但是,从github,翻译过程失败,错误为:“Get/api/projects//progress"404未找到,进度请求失败。此错误在我尝试翻译的所有文件中都很常见,而不是在特定文件中。Node.js服务器上报告的错误为:extract-autodesk-io-2017z52bf5fsa9e17ahxtcraalcq9ercudgg present!由于字符限制,请查看下一篇评论文章部分完成可视化\uu-air.dwg 399147 visualization at离子天线{错误:eNote:没有这样的文件或目录,请打开'D:\Cad Viewer\extract.autodesk.io-master_13july\extract.autodesk.io master\data\399147 visualization aerialdg.json'错误号:-4058,代码:'eNote',系统调用:'open',路径:'D:\\Cad Viewer\\extract.autodesk.io-master u 13july\\extract.autodesk.io master data\\399147 visualization aerialdg.json'}在浏览目录时,我发现了名为“399147 visualization-_aerialdwg.job.json”的文件已创建。消息extract-autodesk-io-2017z52bf5fsa9e17ahxtcraalcq9ercudgg present!不是错误,它表示已创建存储桶,并且安装正确。接下来,您是否对文件夹/data/?和最后一个具有写权限,但理论上可以,进度取决于获取清单,但清单可能还不存在如果Forge translation尚未启动,则会出现此错误。但系统应处理此错误,extract.autodesk.io问题在几分钟前已修复,您能否重试并确认您可以下载?或者将导致问题的模型指向我,thx@PhilippeLeefsma,谢谢你的及时回复。我可以下载bubbles from extract.autodesk.io live demo的更新版本。但是,从github部署示例项目时,翻译过程失败,错误为:“Get/api/projects//progress”404未找到,进度请求失败。此错误在我尝试翻译的所有文件中都很常见,而不是在特定文件中。Node.js服务器上报告的错误为:extract-autodesk-io-2017z52bf5fsa9e17ahxtcraalcq9ercudgg present!由于字符限制,请查看下一篇评论文章部分完成可视化\uu-air.dwg 399147 visualization at离子天线{错误:eNote:没有这样的文件或目录,请打开'D:\Cad Viewer\extract.autodesk.io-master_13july\extract.autodesk.io master\data\399147 visualization aerialdg.json'错误号:-4058,代码:'eNote',系统调用:'open',路径:'D:\\Cad Viewer\\extract.autodesk.io-master u 13july\\extract.autodesk.io master data\\399147 visualization aerialdg.json'}在浏览目录时,我发现了名为“399147 visualization-_aerialdwg.job.json”的文件已创建。消息extract-autodesk-io-2017z52bf5fsa9e17ahxtcraalcq9ercudgg present!不是错误,它表示已创建存储桶,并且安装正确。接下来,您是否对文件夹/data/?和最后一个具有写权限,但理论上可以,进度取决于获取清单,但清单可能还不存在如果伪造的翻译还没有开始,那么就会出现这个错误。但是系统应该处理它,