- Post History
- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
on 09-18-2022 06:31 PM
I recently had a requirement to generate a text file in SN, attach it to a record and then save it to an on-premise network drive. While generating the text file was not a problem, I wasn't sure about saving it to a local network drive. Fortunately, others have already trodden this path before me, so there was some good information on the community site, notably this question. This article is about how I put the logic from that question into a subflow such that it can be easily called from elsewhere.
First, a look at the main parts:
- A MID server script include which saves the file
- A custom flow designer action which calls the mid server script include via the ECC queue.
- Another custom flow designer action to parse the results of the previous script include.
- A subflow which puts the two actions together with a pause in between.
MID Server Script Include
This was my first encounter with this exotic animal. I lifted the code directly from the above mentioned question. Two important points of note:
- The user that your MID Server service is running under has to have permissions to write to your target destination. You can see which user the service is running under by going to Services and clicking on the ServiceNow MID Server service (or whatever your service happens to be named).
- I read somewhere that this will not work for saving files over 5mb to network drives. The MID Server script include uses the Java FileOutputStream which will timeout when writing large files over the network. If you need to save large files it's best to first save them locally to the MID server and then copy them with a powershell script.
Here's the MID Server Script Include (again, credit to this question)
var SaveFile = Class.create();
SaveFile.prototype = {
initialize: function () {
/* Set up the Packages references */
this.File = Packages.java.io.File;
this.FileOutputStream = Packages.java.io.FileOutputStream;
this.HttpClient = Packages.org.apache.commons.httpclient.HttpClient;
this.UsernamePasswordCredentials = Packages.org.apache.commons.httpclient.UsernamePasswordCredentials;
this.AuthScope = Packages.org.apache.commons.httpclient.auth.AuthScope;
this.GetMethod = Packages.org.apache.commons.httpclient.methods.GetMethod;
/* Set up the parameters */
this.verbose = probe.getParameter("verbose");
this.filepath = probe.getParameter("filepath");
this.filename = probe.getParameter("filename");
this.encodedData = probe.getParameter('encodedData');
this.debug("verbose -- filepath -- filename : "+this.verbose + ' -- ' + this.filepath + ' -- ' + this.filename);
},
/* Function to create new file and write data in target path*/
saveToFile: function(targetPath)
{
this.debug("Initiating file save function");
var f = new this.File(targetPath); // create new file
var inputStream = this.encodedData;
var fout = new this.FileOutputStream(f);
this.StringUtil = Packages.com.glide.util.StringUtil;
var data = this.StringUtil.base64DecodeAsBytes(inputStream); // convert base64 to original format
fout.write(data); // write data to newly created file
fout.close();
inputStream.close();
result = "File successfully created : "+this.filepath+this.filename;
this.debug(result);
},
/* Function to debug in mid-server log*/
debug: function (m)
{
if (this.verbose == "true") {
ms.log("Attachment: " + m);
}
},
/* Execute the Probe*/
execute: function()
{
var saveRes = this.saveToFile(this.filepath+this.filename);
return result;
},
type : "SaveFile"
};
Custom Action for Creating an ECC Queue Request
Next we need to create a record in the ECC Queue table that requests that the MID Server run the above MID server script include. I created the below custom flow designer action to do this (note that when I tried to do this with a regular create record action I couldn't get the ecc queue request to process).
- Input the sys_id of the attachment to be copied and the path to copy it to. All paths should have a trailing \, for example C:\temp\.
- Create an ecc_queue record
- Output the sys_id of the ecc_queue record
(function execute(inputs, outputs) {
//from https://community.servicenow.com/community?id=community_question&sys_id=0679b3b0dbfeeb00f21f5583ca961916
//sends base64 encoded attachment to MID server and triggers the SaveFile MID server script include to write the attachment to a network (or local) location
var attGR = new GlideRecord('sys_attachment');
attGR.addQuery('sys_id', inputs.attachment_sysid);
attGR.query();
if (attGR.next())
{
var sa = new GlideSysAttachment();
var encData = sa.getContentBase64(attGR);
var jspr = new global.JavascriptProbe('[MID SERVER NAME]');
jspr.setName('Flow Action: Save File to on-prem network'); // This can be any name
jspr.setJavascript("var ddr = new SaveFile(); res= ddr.execute();");
jspr.addParameter("verbose","true");
jspr.addParameter("filepath", inputs.path); // eg, D://ServiceNow//JiraAttachment// This is optional, if not added, file will be created at rool i.e midserver's agent folder
jspr.addParameter("filename",attGR.file_name);
jspr.addParameter("encodedData",encData);
var eccSysId = jspr.create();
outputs.ecc_sys_id = eccSysId;
}
})(inputs, outputs);
Custom Action for Parsing the ECC Queue Response
We need another custom action to parse the response to the ECC queue request made with the previous custom action.
- Input the payload (this is done by the subflow below)
- Parse the payload
- Output the code and the message
(function execute(inputs, outputs) {
var xmlDoc = new XMLDocument2();
xmlDoc.parseXML(inputs.payload);
//get the value of the result_code attribute
var node = xmlDoc.getNode("//results");
var nodeStr = node.toString()
var start = nodeStr.indexOf('result_code=') + 13
outputs.code = nodeStr.substring(start,start+1);
//get the output
outputs.outmessage = xmlDoc.getNodeText("//output");
})(inputs, outputs);
Subflow for Putting It All Together
The subflow puts the request and response actions together with a pause in between. The subflow can be called from anywhere else in the platform (flows, other subflows, UI actions, etc) which makes it very handy.
- Input the sys_id of the attachment to be saved and the path to which to save it (don't forget the \ at the end!).
- Call the action which creates the ECC Queue request
- Wait for 1 minute (for my purposes this was more than enough, but you might want to make it longer if your files are bigger. I didn't do any tests to see how long bigger files might take).
- Use a look up record action on the ecc_queue table where the Response to is the sys_id of the record created in Step 2.
- Pass the payload field from the record in Step 4 to the Parse ECC Queue response action.
- Output the results.
- 3,675 Views
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
I have a requirement where I have the output of function to write into a file and just transfer to mid server export directory.
Can you please guide how to do the first part. Which take the text output from a sub-flow output and put into a text file and attach as an attachment to a record.
Or
Write that output directly into a new file on midserver (attachment to a record is not compulsory) if we can write directly.
- Mark as Read
- Mark as New
- Bookmark
- Permalink
- Report Inappropriate Content
Note that I was having trouble getting getContentBase64 to work on a Vancouver instance. I replaced it with an encoder suggested by this support article
these are the lines that did not work in Vancouver