Someone asked me if I had ever set up ServiceNow to grab files from an HTTP Resource and set it up as an attachment on a ServiceNow record. I had done something similar a year ago, so I decided to resurrect some code and made a function out of it. This function could be utilized in a business rule, workflow script, or in a Script Include library.
It is a little rough, but I wanted to just present it as an example of how to get an external resource (such as a document) and how to save it as an attachment. It only works for Calgary releases and beyond, but could easily be made more backward compatible.
In the following code example, I also showed how to handle Basic Authentication, should the resource be protected with Basic Auth credentials.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | // // Example of how this function could be called, using some fake resources // var g = new GlideRecord("incident"); g.get("68ce631ce8d20100f8e6928117c296a5"); var remoteFile = "https://john-james-andersen.com/images/Branding038s-1.jpg"; var domain = "www.john-james-andersen.com"; var port = "80"; var contentType = "image/jpeg"; var realm = "Members Area"; var user = "myUser"; var password = "myUserPassword"; attachRemoteFileToRecord(g, remoteFile, contentType, domain, port, realm, user, password); // // The function that does the work // function attachRemoteFileToRecord(gr, url, contentType, domain, port, realm, user, pass) { //This call only works for Calgary -- I could code this up better to work with berlin as well //but I am too lazy var StringUtil = GlideStringUtil; //Set up some critical Java Packages call from the standard Java JDK var httpclient = Packages.org.apache.commons.httpclient; var HttpClient = httpclient.HttpClient; var UsernamePasswordCredentials = httpclient.UsernamePasswordCredentials; var AuthScope = httpclient.auth.AuthScope; var GetMethod = httpclient.methods.GetMethod; var ArrayList = Packages.java.util.ArrayList; var AuthPolicy = Packages.org.apache.commons.httpclient.auth.AuthPolicy; var AuthPNames = Packages.org.apache.http.auth.params.AuthPNames; var NTCredentials = Packages.org.apache.commons.httpclient.NTCredentials; var HttpTransportProperties = Packages.org.apache.axis2.transport.http.HttpTransportProperties; //Instantiate the HTTP Client var client = new HttpClient(); var credentials = new UsernamePasswordCredentials(user, pass); //Handle any authentication var authScope = new AuthScope(domain, port, "Members Area"); client.getState().setCredentials(authScope, credentials); var authPrefs = new ArrayList(1); authPrefs.add(AuthPolicy.BASIC); client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs); gs.log("Getting URL: " + url); var get = new GetMethod(url); gs.log("AuthScope Auth Scheme: " + authScope.getScheme()); gs.log("AuthScope Auth Realm: " + authScope.getRealm()); gs.log("AuthScope Auth Host: " + authScope.getHost()); gs.log("AuthScope Auth Port: " + authScope.getPort()); gs.log("Credentials: " + credentials.getUserName() + " -- " + credentials.getPassword()); var status = client.executeMethod(get); gs.log("Status Line: " + get.getStatusLine()); //The following commented out code can be used for troubleshooting //gs.log("Response Body: " + get.getResponseBodyAsString()); gs.log("--------------Request Headers------------------"); var reqHeaders = get.getRequestHEaders(); for ( var i = 0; i < reqHeaders.length; i++) { gs.log(reqHeaders[i].getName() + " (REQ):" + reqHeaders[i].getValue()); } gs.log("--------------Response Headers------------------"); var headers = get.getResponseHeaders(); for ( var i = 0; i < headers.length; i++) { gs.log(headers[i].getName() + " (RESP) :" + headers[i].getValue()); } //Prepping the input stream and the Glide library for attachments (Calgary format) var is = get.getResponseBodyAsStream(); var sa = new GlideSysAttachment(); var filename = getFileNameFromUrl(url); gs.log("FILENAME: "+filename); //Takes the input stream from the HTTP call and stream it into the attachment record sa.write(gr.sys_id, gr.sys_class_name, filename, contentType, is); //Release the HTTP connection get.releaseConnection(); } function getFileNameFromUrl( url ){ url = ""+url; //remove any anchors url = url.substring(0, (url.indexOf("#") == -1) ? url.length : url.indexOf("#")); //remove any url parameters url = url.substring(0, (url.indexOf("?") == -1) ? url.length : url.indexOf("?")); //remove domain and path portions url = url.substring(url.lastIndexOf("/") + 1, url.length); return url; } |
Once this example script is run, I can go to the incident table and browse to the record with the matching sys_id value. There I will find the file attached to my record:
Can you tell me what would need to change to make it Berlin-compatible?
It is only in the packages calls that were in the Calgary format. I would believe you could just change the following lines of code:
Line 27
FROM: var StringUtil = GlideStringUtil;
TO: var StringUtil = Packages.com.glide.util.StringUtil;
Line 81
FROM: var sa = new GlideSysAttachment();
TO: var sa = new Packages.com.glide.ui.SysAttachment();
Hi John,
Thanks for this post! Helped me a lot!
I just did a little change on ContentType:
var contentType = headers[3].getValue();
contentType = contentType.slice(contentType.indexOf(‘:’) + 1,contentType.indexOf(‘;’) + 1);
This way the contentType parameter will be dynamic.
Thanks again!
Hey John, this now longer seems to work on Dublin (for me at least) – I get error:
Can’t find method com.glide.ui.SysAttachment.get(string,string).
@Dan, ServiceNow continues to lock down more GlideScriptable and Java JDK library access. This is likely why you are seeing the issue. I will probably retire this blog post due to those changes.
Hi John,
This is great. I am able to use your script and attach the pdf file which i generated from the same form.
So basically what i am doing is accessing (https://.service-now.com/u_status_reports.do?PDF&sys_id=) as remoteFile url.
Only thing i want to get around now is authentication part, is there any possibility that i can pass session id to this instead or user name password or completely ignore passing the credentials, will it work ?
earlier help will be appreciated.
Cheers
If you’re getting an error: Can’t find method com.glide.ui.SysAttachment.get(…)
Change line 87
FROM: sa.write(gr.sys_id, gr.sys_class_name, filename, contentType, is);
TO: sa.write(gr, filename, contentType, is);
Apparently method signature changed in Dublin? The revised signature works in Eureka.
Hello John,
I am getting error while using getResponseBodyAsStream() saying “Illegal access to Method getResponseBodyAsStream()”