cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Content Types Returned from Axeda Scripto

Highlighted
Marble

Content Types Returned from Axeda Scripto

This tutorial applies to Axeda version 6.1.6+, with sections applicable to 6.5+ (indicated below)


Custom objects (or Groovy scripts) are the backbone of Axeda custom applications.  As the developer, you decide what content type to give the data returned by the script.

What this tutorial covers?

This tutorial provides examples of outputting data in different formats from Groovy scripts and consuming that data via Javascript using the jQuery framework.  While Javascript and jQuery are preferred by the Axeda Innovation team, any front end technology that can consume web services can be used to build applications on the Axeda Machine Cloud.  On the same note, the formats discussed in this article are only a few examples of the wide variety of content types that Groovy scripts can output via Scripto.  The content types available via Scripto are limited only by their portability over the TCP protocol, a qualification which includes all text-based and downloadable binary mime types.  As of July 2013, the UDP protocol (content streaming) is not supported by the current version of the Axeda Platform.

Formats discussed in this article:

1) JSON
2) XML
3) CSV
4) Binary content with an emphasis on image files (6.5+)


For a tutorial on how to create custom objects that work with custom applications, check out Using Google Charts API with ScriptoFor a discussion of what Scripto is and how it relates to Groovy scripts and Axeda web services, take a look at Unleashing the Power of the Axeda Platform via Scripto.


Serializing Data


JSON

For those building custom applications with Javascript, serializing data from scripts into JSON is a great choice, as the data is easily consumable as native Javascript objects.

The net.sf.json JSON library is available to use in the SDK.  It offers an easy way to serialize objects on the Platform, particularly v2 SDK objects.

import net.sf.json.JSONArray
import static com.axeda.sdk.v2.dsl.Bridges.*

def asset = assetBridge.findById(parameters.assetId)

def response = JSONArray.fromObject(asset).toString(2)

return ["Content-Type": "application/json", "Content": response]

Outputs:

[{

    "buildVersion": "",

    "condition": {

        "detail": "",

        "id": "3",

        "label": "",

        "restUrl": "",

        "systemId": "3"

    },

    "customer": {

        "detail": "",

        "id": "2",

        "label": "Default Organization",

        "restUrl": "",

        "systemId": "2"

    },

    "dateRegistered": {

        "date": 11,

        "day": 1,

        "hours": 18,

        "minutes": 7,

        "month": 2,

        "seconds": 49,

        "time": 1363025269253,

        "timezoneOffset": 0,

        "year": 113

    },

    "description": "",

    "detail": "testasset",

    "details": null,

    "gateways": [],

    "id": "12345",

    "label": "",

    "location": {

        "detail": "Default Organization",

        "id": "2",

        "label": "Default Location",

        "restUrl": "",

        "systemId": "2"

    },

    "model": {

        "detail": "testmodel",

        "id": "2345",

        "label": "standalone",

        "restUrl": "",

        "systemId": "2345"

    },

    "name": "testasset",

    "pingRate": 0,

    "properties": [

        {

            "detail": "",

            "id": "1",

            "label": "TestProperty",

            "name": "TestProperty",

            "parentId": "2345",

            "restUrl": "",

            "systemId": "1",

            "value": ""

        },

        {

            "detail": "",

            "id": "4",

            "label": "TestProperty0",

            "name": "TestProperty0",

            "parentId": "2345",

            "restUrl": "",

            "systemId": "4",

            "value": ""

        },

        {

            "detail": "",

            "id": "3",

            "label": "TestProperty1",

            "name": "TestProperty1",

            "parentId": "2345",

            "restUrl": "",

            "systemId": "3",

            "value": ""

        },

        {

            "detail": "",

            "id": "2",

            "label": "TestProperty2",

            "name": "TestProperty2",

            "parentId": "2345",

            "restUrl": "",

            "systemId": "2",

            "value": ""

        }

    ],

    "restUrl": "",

    "serialNumber": "testasset",

    "sharedKey": [],

    "systemId": "12345",

    "timeZone": "GMT"

}]

This output can be traversed as Javascript object with its nodes accessible using dot (.) notation.

For example, if you set the above JSON as the content of variable "json", you can access it in the following way, without any preliminary parsing needed:

assert json[0].condition.id == 3

If you use jQuery, a Javascript library, feel free to make use of axeda.js, which contains utility functions to pass data to and from the Axeda Platform.  One function in particular is used in most example custom applications found on this site, the axeda.callScripto function.  It relies on the jQuery ajax function to make the underlying call.

/**

  * makes a call to the enterprise platform services with the name of a script and passes

  * the script any parameters provided.

  *

  * default is GET if the method is unknown

  *

  * Notes: Added POST semantics - plombardi @ 2011-09-07

  *

  * original author: Zack Klink & Philip Lombardi

  * added on: 2011/7/23

  */

// options - localstoreoff: "yes" for no local storage, contentType: "application/json; charset=utf-8",

axeda.callScripto = function (method, scriptName, scriptParams, attempts, callback, options) {

  var reqUrl = axeda.host + SERVICES_PATH + 'Scripto/execute/' + scriptName + '?sessionid=' + SESSION_ID

  var contentType = options.contentType ? options.contentType : "application/json; charset=utf-8"

  var local

  var daystring = keygen()

  if (options.localstoreoff == null) {

  if (localStorage) {

  local = localStorage.getItem(scriptName + JSON.stringify(scriptParams))

  }

  if (local != null && local == daystring) {

  return dfdgen(reqUrl + JSON.stringify(scriptParams))

  } else {

  localStorage.setItem(scriptName + JSON.stringify(scriptParams), daystring)

  }

  }

  return $.ajax({

  type: method,

  url: reqUrl,

  data: scriptParams,

  contentType: contentType,

  dataType: "text",

  error: function () {

  if (attempts) {

  expiredSessionLogin();

  setTimeout(function () {

  axeda.callScripto('POST', scriptName, scriptParams, attempts - 1, callback, options)

  }, 1500);

  }

  },

  success: function (data) {

  if (options.localstoreoff == null) {

  localStorage.setItem(reqUrl + JSON.stringify(scriptParams), JSON.stringify([data]))

  }

  if (contentType.match("json")) {

  callback(unwrapResponse(data))

  } else {

  callback(data)

  }

  }

  })

};

Using the axeda.callScripto function:

var postToPlatform = function (scriptname, callback, map) {

        var options = {
            localstoreoff: "yes",
            contentType: "application/json; charset=utf-8"

        }
 

     // Javascript object "map" has to be stringified to post to Axeda Platform

        axeda.callScripto("POST", scriptname, JSON.stringify(map), 2, function (json) {

            // callback gets the JSON object output by the Groovy script
            callback(json)

        }, options)
    }

The JSON object is discussed in more detail here.

Back to Top

XML

XML is the preferred language of integration with external applications and services. Groovy provides utilities to make XML serialization a trivial exercise.

import groovy.xml.MarkupBuilder

import static com.axeda.sdk.v2.dsl.Bridges.*

def writer = new StringWriter()
def xml = new MarkupBuilder(writer)

def findAssetResult = assetBridge.find(new AssetCriteria(modelNumber: parameters.modelName))

// find operation returns AssetReference class. Contains asset id only
def assets = findAssetResult.assets
  
  xml.Response() {
  Assets() {
  assets.each { AssetReference assetRef ->
  def asset = assetBridge.findById(assetRef.id)

              // asset contains a ModelReference object instead of a Model.  ModelReference has a detail property, not a name property

  Asset() {
  id(asset.id)
  name(asset.name)
  serial_number(asset.serialNumber)
  model_id(asset.model.id)
  model_name(asset.model.detail)
  }
  }
  }
  }

return ['Content-Type': 'text/xml', 'Content': writer.toString()]

Output:

<Assets>

  <Asset>

  <id>98765</id>

  <name>testasset</name>

  <serial_number>testasset</serial_number>

  <model_id>4321</model_id>

  <model_name>testmodel</model_name>

  </Asset>

</Assets

Although XML is not a native Javascript object as is JSON, Javascript libraries and utilities are available for parsing XML into an object traversable in Javascript.

For more information on parsing XML in Javascript, see W3 Schools XML Parser.  For those using jQuery, check out the jQuery.parseXML function.

Back to Top

Outputting Files (Binary content types)


CSV


CSV comes in handy for spreadsheet generation as it is compatible with Microsoft Excel.

The following example is suitable for Axeda version 6.1.6+ as it makes use of the Data Accumulator feature to create a downloadable file.

import com.axeda.drm.sdk.device.ModelFinder

import com.axeda.drm.sdk.Context

import com.axeda.drm.sdk.scripto.Request

import com.axeda.common.sdk.id.Identifier

import com.axeda.drm.sdk.device.Model

import com.axeda.drm.sdk.device.DataItem

import com.axeda.drm.sdk.device.DataItemValue

import com.axeda.drm.sdk.data.DataValue

import com.axeda.drm.sdk.device.DeviceFinder

import com.axeda.drm.sdk.device.Device

import com.axeda.drm.sdk.mobilelocation.MobileLocation

import com.axeda.drm.sdk.data.DataValueList

import com.axeda.drm.sdk.data.CurrentDataFinder

import com.axeda.drm.sdk.mobilelocation.CurrentMobileLocationFinder

import groovy.xml.MarkupBuilder

import com.axeda.platform.sdk.v1.services.ServiceFactory

/*

* ExportObjectToCSV.groovy

*

* Creates a csv file from either all assets of a model of a single asset that can then be used to import them back into another system.

*

* @param model        -   (REQ):Str model name.

* @param serial        -   (OPT):Str serial number.

*

* @author Sara Streeter <sstreeter@axeda.com>

*/

def writer = new StringWriter()

def xml = new MarkupBuilder(writer)

InputStream is

try {

   Context CONTEXT = Context.getSDKContext()

   ModelFinder modelFinder = new ModelFinder(CONTEXT)

    modelFinder.setName(Request.parameters.model)

   Model model = modelFinder.find()

   DeviceFinder deviceFinder = new DeviceFinder(CONTEXT)

   deviceFinder.setModel(model)

   List<Device> devices = []

def exportkey = model.name

Device founddevice

if (Request.parameters.serial){

    deviceFinder.setSerialNumber(Request.parameters.serial)

   founddevice = deviceFinder.find()

   logger.info(founddevice?.serialNumber)

   if (founddevice != null){

   devices.add(founddevice)

   }

   else throw new Exception("Device ${Request.parameters.serial} cannot be found.")

   exportkey += "${founddevice.serialNumber}"

}

else {

    devices = deviceFinder.findAll()

    exportkey += "all"

}

// use a Data Accumulator to store the information

def dataStoreIdentifier = "FILE-CSV-export_____" + exportkey

def daSvc = new ServiceFactory().dataAccumulatorService

if (daSvc.doesAccumulationExist(dataStoreIdentifier, devices[0].id.value)) {

  daSvc.deleteAccumulation(dataStoreIdentifier, devices[0].id.value)

}

List<DataItem> dataItemList = devices[0].model.dataItems

def firstrow = [ "model", "serial", "devicename", "conditionname", "currentlat","currentlng" ]

           

        def tempfirstrow = dataItemList.inject([]){list, dataItem ->

            list << dataItem.name;

            list

        }

        firstrow += tempfirstrow   

        firstrow = firstrow.join(',')

        firstrow += '\n'

        daSvc.writeChunk(dataStoreIdentifier, devices[0].id.value, firstrow);

   

CurrentMobileLocationFinder currentMobileLocationFinder = new CurrentMobileLocationFinder(CONTEXT)

devices.each{ device ->

                CurrentDataFinder currentDataFinder = new CurrentDataFinder(CONTEXT, device)

                currentMobileLocationFinder.deviceId = device.id.value

                MobileLocation mobileLocation = currentMobileLocationFinder.find()

                def lat = 0

                def lng = 0

                if (mobileLocation){

                    lat = mobileLocation?.lat

                    lng = mobileLocation?.lng

                }

                def row =

                [

                    device.model.name,

                    device.serialNumber,

                    device.name,

                    device.condition?.name,

                    lat,

                    lng

                    ]

               

                    def temprow = dataItemList.inject([]){ subList,dataItem ->

                        DataValue value = currentDataFinder.find(dataItem.name)

                   

                        def val = "NULL"

                        val = value?.asString() != "?" ? value?.asString() : val

                        subList <<  val

                        subList

                    }

                row += temprow

                row = row.join(',')

                row += '\n'

                daSvc.writeChunk(dataStoreIdentifier, devices[0].id.value, row);

            }

  

// stream the data accumulator to create the file

is = daSvc.streamAccumulation(dataStoreIdentifier, devices[0].id.value)

def disposition = 'attachment; filename=CSVFile' + exportkey + '.csv'

return ['Content-Type': 'text/csv', 'Content-Disposition':disposition, 'Content': is.text]

} catch (def ex) {

   xml.Response() {

       Fault {

           Code('Groovy Exception')

           Message(ex.getMessage())

           StringWriter sw = new StringWriter();

           PrintWriter pw = new PrintWriter(sw);

           ex.printStackTrace(pw);

           Detail(sw.toString())

       }

   }

logger.info(writer.toString())

return ['Content-Type': 'text/xml', 'Content': writer.toString()]

}

return ['Content-Type': 'text/xml', 'Content': writer.toString()]

Back to Top

Image Files (6.5+)


The FileStore in Axeda version 6.5+ allows fine-grained control of uploaded and downloaded files. As Groovy scripts can return binary data via Scripto, this allows use cases such as embedding a Groovy script url as the source for an image.

The following example uses the FileStore API to create an Image out of a valid image file, scales it to a smaller size and stores this smaller file.

import com.axeda.drm.sdk.Context

import com.axeda.drm.sdk.data.*

import com.axeda.drm.sdk.device.*

import com.axeda.drm.sdk.mobilelocation.CurrentMobileLocationFinder

import com.axeda.drm.sdk.mobilelocation.MobileLocation

import com.axeda.drm.sdk.mobilelocation.MobileLocationFinder

import com.axeda.sdk.v2.bridge.FileInfoBridge

import static com.axeda.sdk.v2.dsl.Bridges.*

import com.axeda.services.v2.ExecutionResult

import com.axeda.services.v2.FileInfo

import com.axeda.services.v2.FileInfoReference

import com.axeda.services.v2.FileUploadSession

import net.sf.json.JSONObject

import groovy.json.JsonBuilder

import net.sf.json.JSONArray

import com.axeda.drm.sdk.scripto.Request

import org.apache.commons.io.IOUtils

import org.apache.commons.lang.exception.ExceptionUtils

import com.axeda.common.sdk.id.Identifier

import groovy.json.*

import javax.imageio.ImageIO;

import java.awt.RenderingHints

import java.awt.image.BufferedImage

import java.io.ByteArrayOutputStream;

import java.awt.*

import java.awt.geom.*

import javax.imageio.*

import java.awt.image.*

import java.awt.Graphics2D

import javax.imageio.stream.ImageInputStream

/*

   Image-specific FileStore entry point to post and store files

*/

def contentType = "application/json"

final def serviceName = "StoreScaledImage"

// Create a JSON Builder

def json = new JsonBuilder()

// Global try/catch. Gotta have it, you never know when your code will be exceptional!

try {

 

    Context CONTEXT = Context.getSDKContext()

    def filesList = []

    def datestring = new Date().time

    InputStream inputStream = Request.inputStream

 

    def reqbody = Request.body

    // all of our Request Parameters are available here

    def params = Request.parameters

    def filename = Request?.headers?.'Content-Disposition' ?

    Request?.headers?.'Content-Disposition' : "file___" + datestring + ".txt"

    def filelabel = Request.parameters.filelabel ?: filename

    def description = Request.parameters.description ?: filename

    def contType = Request.headers?."content-type" ?: "image/jpeg"

    def tag = Request.parameters.tag ?: "cappimg"

    def encoded = Request.parameters.encoded?.toBoolean()

  def dimlimit = params.dimlimit ? params.dimlimit : 280

    // host is available in the headers when the script is called with AJAX

    def domain = Request.headers?.host

    byte[] bytes = IOUtils.toByteArray(inputStream);

    def fileext = filename.substring(filename.indexOf(".") + 1,filename.size())

    def outerMap = [:]

    // check that file extension matches an image type

    if (fileext ==~ /([^\s]+(\.(?i)(jpg|jpeg|png|gif|bmp))$)/){

        if (inputStream.available() > 0) {

                def scaledImg

             

                try {

                    def img = ImageIO.read(inputStream)

                    def width = img?.width         

                    def height = img?.height

                    def ratio = 1.0

                    def newBytes

                 

                    if (img){

                     

                        if (width > dimlimit || height > dimlimit){

                            // shrink by the smaller side so it can still be over the limit

                            def dimtochange = width > height ? height : width

                            ratio = dimlimit / dimtochange

                         

                            width = Math.floor(width * ratio).toInteger()

                            height = Math.floor(height * ratio).toInteger()

                        }

                     

                      newBytes = doScale(img, width, height, ratio, fileext)

                     if (newBytes?.size() > 0){

                        bytes = newBytes

                     }

                    }

                }

                catch(Exception e){

                    logger.info(e.localizedMessage)

                 

                }

                         

                outerMap.byteCount = bytes.size()

  

                FileInfoBridge fib = fileInfoBridge

                FileInfo myImageFile = new FileInfo(filelabel: filelabel,

                                                    filename: filename,

                                                    filesize: bytes?.size(),

                                                    description: description,

                                                    tags: tag

                                                    )

  

                myImageFile.contentType = contType

  

                FileUploadSession fus = new FileUploadSession();

                fus.files = [myImageFile]

  

                ExecutionResult fer = fileUploadSessionBridge.create(fus);

                myImageFile.sessionId = fer.succeeded.getAt(0)?.id

             

                ExecutionResult fileInfoResult = fib.create(myImageFile)

             

                if (fileInfoResult.successful) {

                    outerMap.fileInfoSave = "File Info Saved"

                    outerMap.sessionId = "File Upload SessionID: "+fer.succeeded.getAt(0)?.id

                    outerMap.fileInfoId = "FileInfo ID: "+fileInfoResult?.succeeded.getAt(0)?.id

                    ExecutionResult er = fib.saveOrUpdate(fileInfoResult.succeeded.getAt(0).id,new ByteArrayInputStream(bytes))

                    def fileInfoId = fileInfoResult?.succeeded.getAt(0)?.id

                    String url = "${domain}/services/v1/rest/Scripto/execute/DownloadFile?fileId=${fileInfoId}"

                    if (er.successful) {

                        outerMap.url = url

                    } else {

                        outerMap.save = "false"

                        logger.info(logFailure(er,outerMap))

                    }

                } else {

                    logger.info(logFailure(fileInfoResult, outerMap))

                }

  

            } else {

                outerMap.bytesAvail = "No bytes found to upload"

            }

        } else {

            outerMap.imagetype = "Extension $fileext is not a supported image file type."

        }

    filesList << outerMap

    // return the JSONBuilder contents

    // we specify the content type, and any object as the return (even an outputstream!)

    return ["Content-Type": contentType,"Content":JSONArray.fromObject(filesList).toString(2)]

    // alternately you may just want to serial an Object as JSON:

    // return ["Content-Type": contentType,"Content":JSONArray.fromObject(invertedMessages).toString(2)]

} catch (Exception e) {

    // I knew you were exceptional!

    // we'll capture the output of the stack trace and return it in JSON

    json.Exception(

            description: "Execution Failed!!! An Exception was caught...",

            stack: ExceptionUtils.getFullStackTrace(e)

    )

    // return the output

    return ["Content-Type": contentType, "Content": json.toPrettyString()]

}

def doScale(image, width, height, ratio, fileext){

    if (image){

    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    def bytes

     def scaledImg = new BufferedImage( width, height, BufferedImage.TYPE_INT_RGB )

       Graphics2D g = scaledImg.createGraphics();

        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);

        g.scale(ratio,ratio)

        g.drawImage(image, null, null);

        g.dispose();

     

       ImageIO.write( scaledImg, fileext, baos )

      baos.flush()

      bytes = baos.toByteArray()

      baos.close()

    }

    else {

        logger.info("image to be scaled is null")

        return false

    }

  return bytes  

}

private void logFailure(ExecutionResult fileInfoResult, LinkedHashMap outerMap) {

    outerMap.message = fileInfoResult.failures.getAt(0)?.message

    outerMap.source = fileInfoResult.failures.getAt(0)?.sourceOfFailure

    outerMap.details = fileInfoResult.failures.getAt(0)?.details?.toString()

    outerMap.fileInfoSave = "false"

}

The next example makes use of the jQuery framework to upload an image to this script via an http POST.

Note: This snippet is available as a jsFiddle at http://jsfiddle.net/LrxWF/18/


With HTML5 button:

<input type="file" id="fileinput" value="Upload" />

var PLATFORM_HOST = document.URL.split('/apps/')[0]; // this is how you would retrieve the host on an Axeda instance

var SESSION_ID = null // usually retrieved from login function included below

/***

* Depends on jQuery 1.7+ and HTML5, assumes an HTML5 element such as the following:

* <input type="file" id="fileinput" value="Upload" />

* **/

$("#fileinput").off("click.filein").on("click.filein", function () {

    fileUpload()

})

var fileUpload = function () {

    $("#fileinput").off('change.fileinput')

    $("#fileinput").on('change.fileinput', function (event) {

        if (this.files && this.files.length > 0) {

            handleFiles("http://" + PLATFORM_HOST, this.files)

        }

    })

}

var handleFiles = function (host, files) {

    $.each(files, function (index, file) {

        var formData = new FormData();

        var filename = file.name

        formData.append(filename, file)

        var url = host + '/services/v1/rest/Scripto/execute/StoreScaledImage?filelabel=' + filename + "&tag=myimg"

        url = setSessionId(url)

        jQuery.ajax(url, {

            beforeSend: function (xhr) {

                xhr.setRequestHeader('Content-Disposition', filename);

            },

            cache: false,

            cache: false,

            processData: false,

            type: 'POST',

            contentType: false,

            data: formData,

            success: function (json) {

                refreshPage(json)

                console.log(json)

            }

        });

    })

}

var setSessionId = function (url) {

    // you would already have this from logging in

    return url + "&sessionid=" + SESSION_ID

}

var refreshPage = function (json) {

    // here you would refresh your page with the returned JSON

    return

}

/***

*  The following functions are not used in this demonstration, however they are necessary for a complete app and are found in axeda.js  http://gist.github.com/axeda/4340789

***/

    function login(username, password, success, failure) {

        var reqUrl = host + SERVICES_PATH + 'Auth/login';

        localStorage.clear()

        return $.get(reqUrl, {

            'principal.username': username,

                'password': password

        }, function (xml) {

            var sessionId = $(xml).find("ns1\\:sessionId, sessionId").text()

            // var sessionId = $(xml).find("[nodeName='ns1:sessionId']").text(); - no longer works past jquery 1.7

            if (sessionId) {

                // set the username and password vars for future logins.        

                storeSession(sessionId);

                success(SESSION_ID); // return the freshly stored contents of SESSION_ID

            } else {

                failure($(xml).find("faultstring").text());

            }

        }).error(function () {

            $('#loginerror').html('Login Failed, please try again')

        });

    };

function storeSession(sessionId) {

    var date = new Date();

    date.setTime(date.getTime() + SESSION_EXPIRATION);

    SESSION_ID = sessionId

    document.cookie = APP_NAME + '_sessionId=' + SESSION_ID + '; expires=' + date.toGMTString() + '; path=/';

    return true;

};

The return JSON includes a URL that you can use as the source for images:

[{

  "byteCount": 14863,

  "fileInfoSave": "File Info Saved",

  "sessionId": "File Upload SessionID: 01234",

  "fileInfoId": "FileInfo ID: 12345",

  "url": "http://yourdomain.axeda.com/services/v1/rest/Scripto/execute/DownloadFile?fileId=12345"

}]

The DownloadFile Custom Object looks like the following:

import static com.axeda.sdk.v2.dsl.Bridges.*

import javax.activation.MimetypesFileTypeMap

import com.axeda.services.v2.*

import com.axeda.sdk.v2.exception.*

import com.axeda.drm.sdk.scripto.Request

def knowntypes = [

         [png: 'image/png']

        ,[gif: 'image/gif']

        ,[jpg: 'image/jpeg']

    ]

def params = Request.parameters.size() > 0 ? Request.parameters : parameters

def response = fileInfoBridge.getFileData(params.fileId)

def fileinfo = fileInfoBridge.findById(params.fileId)

def type = fileinfo.filename.substring(fileinfo.filename.indexOf('.') + 1,fileinfo.filename.size())

type = returnType(knowntypes, type)

def contentType = params.type ?: (type ?: 'image/jpg')

return ['Content': response, 'Content-Disposition': contentType, 'Content-Type':contentType]

def returnType(knowntypes, ext){

    return knowntypes.find{ it.containsKey(ext) }?."$ext"

}

Make sure to append a valid session id to the end of the URL when using it as the source for an image.

The techniques discussed above can be applied to any type of binary file output with consideration for the type of file being processed.

A Word on Streaming

Content streaming such as streaming of video or audio files over UDP is not currently supported by the Axeda Platform.

Tags (2)