Refering to this post, [Download all the derivatives for the purpose of the using Forge Viewer offline/C#, I know that the files provided by derivative api "GET :urn/manifest/:derivativeurn" are enough for offline viewing. However using the same as input in offline viewer isn't working. Analyzing the demo at https://extract.autodesk.io for existing sample files indicated that downloaded bubble has other files too like bin/pack/zip/ json, where can I get these files using C# Api ? Uploading a sample file and using model extractor returned error ("Cannot GET /extracted/1906495-seatdwf.zip")[As per the suggestion, tried extract.autodesk.io version of March 24,but to no use.] Please guide on how to download required files for offline viewing using C#. Thanks in advance.
1
votes
The extract.autodesk.io issue was fixed a few minutes ago, can you try again and confirm that you can get your download? or point me to the model which cause a problem, thx
– cyrille
@Philippe Leefsma, Thanks for prompt response.I am able to download bubbles from the updated version for extract.autodesk.io live demo. However deploying the sample project from github, translation process fails with error: "Get /api/projects/<root>/progress" 404 not found, Progress request failed. This error is common for all files that I try to translate, not for a particular file. Error reported on Node.js server is: extract-autodesk-io-2017z52bf5fsa9e17ahxtcraalcq9ercudgg present! Due to character limit please check next comment
– Hardik Dobariya
POST partly_done visualization_-aerial.dwg 399147-visualization-aerialdwg { Error: ENOENT: no such file or directory, open 'D:\Cad Viewer\extract.autodesk.io-master_13july\extract.autodesk.io-master\data\399147-visualization-aerialdwg.json' errno: -4058, code: 'ENOENT', syscall: 'open', path: 'D:\\Cad Viewer\\extract.autodesk.io-master_13july\\extract.autodesk.io-master\\data\\399147-visualization-aerialdwg.json' } While exploring the directory, I found that file with name "399147-visualization-_aerialdwg.job.json" is created.
– Hardik Dobariya
the message extract-autodesk-io-2017z52bf5fsa9e17ahxtcraalcq9ercudgg present! is not an error, it says that the bucket was already created and that the setup is correct. Next, do you have write permissions on folder /data/ ? and last but in theory it is ok, the progress relies on getting the manifest, but the manifest may not exist yet if the Forge translation hasn't kick-off yet, hence this error. But the system should cope with it,
– cyrille
1 Answers
0
votes
Here is a clear commented Node.js version in ES6+async of the extractor that I wrote inspired from extract.autodesk.io:
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)
}
})
}
}
An example of use is as follow:
// name of model to download
const name = 'MyForgeModel'
// URN of model to download
const urn = 'dXGhsujdj .... '
// Get Forge service
const forgeSvc = ServiceManager.getService(
'ForgeSvc')
// getToken async function
const getToken = () => forgeSvc.get2LeggedToken()
// Get Extractor service
const extractorSvc = ServiceManager.getService(
'ExtractorSvc')
// target path to download SVF
const dir = path.resolve(__dirname, `${name}`)
// perform download
const files = await extractorSvc.download(
getToken, urn, dir)
// target zipfile
const zipfile = dir + '.zip'
// zip all files
await extractorSvc.createZip(
dir, zipfile, name, files)
// remove downloaded resources directory
rmdir(dir)