Skip to main content

How To Download Files

To explain how to download files in DynaMaker we take four different examples:

To keep the code clean, the data needed to generate the file is usually done through actions since we want to keep all the logic related to the component in an action and not the UI; while exporting the data is done through a button in the user interface (UI)

A. Download Single Files

STL & PLY

  • Generate the STL or PlY data from the model through an action.
  • In UI, create a button including that action like:
const downloadStlButton = new SKYUI.Button('Download STL', () => {
const stlData = ACTIONS.generateStlData()
Studio.downloadFile('myFile.stl', stlData)
})

const downloadPlyButton = new SKYUI.Button('Download PLY', () => {
const plyData = ACTIONS.generatePlyData()
Studio.downloadFile('myFile.ply', plyData)
})

PDF & DXF

  • In your drawing exporter (e.g. ComponentDrawing in the default project template), make sure to generate your drawing.
  • In UI, generate the file through an action in the desired format:
function generateDrawing() {
const component = getComponent() // function already in template
const drawing = COMPONENTDRAWING.generateDrawing()
return drawing
}

export function generateComponentPdfFile() {
const drawing = generateDrawing()
const pdfFile = drawing.generatePdfFile()
return pdfFile
}

export function generateComponentDxfFile() {
const drawing = generateDrawing()
const dxfFile = drawing.generateDxfFile()
return dxfFile
}
  • Create a button including that action like:
const downloadPdfButton = new SKYUI.Button('Download PDF', async () => {
try {
const pdfFile = await ACTIONS.generateComponentPdfFile()
Studio.downloadFile('myDrawing.pdf', pdfFile)
} catch (error) {
console.error(error)
Studio.displayNotification(`Could not generate PDF: ${error.message}`, { type: 'danger' })
}
})

const downloadDxfButton = new SKYUI.Button('Download DXF', async () => {
try {
const dxfFile = await ACTIONS.generateComponentDxfFile()
Studio.downloadFile('myDrawing.dxf', dxfFile)
} catch (error) {
console.error(error)
Studio.displayNotification(`Could not generate DXF: ${error.message}`, { type: 'danger' })
}
})

Since they are both Promises, you need to have the function as async, so that you can await > ACTIONS.generateComponentPdfFile() and ACTIONS.generateComponentDxfFile(). Also, async functions allow you to catch any possible errors. Although it is highly recommended to use, you can also skip it and just have the button as:

const downloadPdfButton = new SKYUI.Button('Download PDF', async () => {
const pdfFile = await ACTIONS.generateComponentPdfFile()
Studio.downloadFile('myDrawing.pdf', pdfFile)
})

B. Download Multiple Files

This button downloads two files (e.g. STL and PDF) at the same time.

const downloadFilesButton = new SKYUI.Button('Download files', async () => {
const stlData = ACTIONS.generateStlData()
Studio.downloadFile('myFile.stl', stlData)

try {
const pdfFile = await ACTIONS.generateComponentPdfFile()
Studio.downloadFile('myDrawing.pdf', pdfFile)
} catch (error) {
console.error(error)
Studio.displayNotification(`Could not generate PDF: ${error.message}`, { type: 'danger' })
}
})

C. Download Compressed File (Without Promises)

This button downloads a ZIP file with 2 different STL files

const downloadZipButton = new SKYUI.Button('Download ZIP', () => {
const stlData1 = ACTIONS.generateStlData1()
const stlData2 = ACTIONS.generateStlData2()

Studio.compress({ name: 'myFile1.stl', data: stlData1 }, { name: 'myFile2.stl', data: stlData2 }).then((zip) => {
Studio.downloadFile('myStlFiles.zip', zip)
})
})

Notice that Studio.compress() can be used directly because there is no data to be 'processed', which is not the case when we have drawings. For this, see the next example.

D. Download Compressed File (With Promises)

This button downloads a ZIP file with:

  • 2 different STL files
  • 1 XML file
  • 1 BOM (.txt) file
  • as many PDF & DXF files as components exist
const downloadZIPButton = new SKYUI.Button('Download ZIP', () => {
const pdfFiles = []
const dxfFiles = []
const xmlFiles = []
const bomFiles = []

// 2 STL
const stlData1 = ACTIONS.generateStlData()
const stlData2 = ACTIONS.generateStlData()

// PDF + DXF per component
const components = [1, 2, 3, 4, 5, 6, 7, 8, 9] // replace with your components

components.forEach((component, componentIndex) => {
const componentId = components[componentIndex] // replace with your naming convention (e.g. component.getName()), 'componentIndex' is used here as an example

const pdfFile = { data: ACTIONS.generateComponentPdfFile(), name: `myPDF${componentId}.pdf` }
pdfFiles.push(pdfFile)

const dxfFile = { data: ACTIONS.generateComponentDxfFile(), name: `myDXF${componentId}.dxf` }
dxfFiles.push(dxfFile)
})

// 1 XML
const xml: string = ACTIONS.generateXml()
const xmlFile = { data: xml, name: `myXML.xml` }
xmlFiles.push(xmlFile)

// 1 BOM (.txt)
const bomList: string[] = ACTIONS.generateBom()
const bomFile = { data: bomList.join('\n'), name: `myBOM.txt` }
bomFiles.push(bomFile)

const filesWithDataToResolve = [].concat(pdfFiles, dxfFiles) // these are { data: Promise<Blob>, name: string }
const dataListToResolve = filesWithDataToResolve.map((file) => file.data)

Promise.all(dataListToResolve).then((dataListResolved) => {
// Match the data resolved with the file to get the name for the file
const filesWithDataResolved = dataListResolved.map((data, index) => {
return { data, name: filesWithDataToResolve[index].name }
})

Studio.compress(
{ name: 'myStl1.stl', data: stlData1 },
{ name: 'myStl2.stl', data: stlData2 },
...xmlFiles,
...bomFiles,
...filesWithDataResolved,
).then((zip) => {
Studio.downloadFile('my22Files.zip', zip)
})
})
})

If you debug this function, you will see that the drawings are Promises and they need to be processed somehow. For that, Promise.all() is used, giving the data needed to download. Then once we match the data with the name, we can get the standardized structure { data, name } used for compressing the files into a single zip.

Here is an app including all the four mentioned examples using exactly the code above: