The CreatorCon Call for Content is officially open! Get started here.

How to add an attachment to a record using a file input on a UI Page (custom development).

nick0989
Tera Contributor

I have a form on a UI page and all I need to do is take an input field with type "file", and have the user upload a file. Simple enough.

The file then needs to be added to a record as an attachment. I have the record in the form.

I can't figure out how to add the file as an attachment, I have tried GlideSysAttachment().

var fileName = this.getParameter('sysparm_file_name'); //file name from input
var fileType = this.getParameter('sysparm_file_type'); //file type
var tableName = this.getParameter('sysparm_table_name'); //the table of the record
var task = this.getParameter('sysparm_task'); //the record

var attachment = new GlideSysAttachment();

var rec = new GlideRecord(tableName);
rec.get(task);
var file = fileName;
var contentType = fileType;
var content = ""; //I am unsure if I can put anything here?
var agr = attachment.write(rec, file, contentType, content);
return agr;

I am unsure as to if this will work with file types.

I have tried the api/now/attachment/file end point to POST.

/api/now/attachment/file?table_name=" + tableName + "&table_sys_id=" + task + "&file_name=" + fileName

But this doesn't seem to work either.

I have to use a input element with type file in the UI page.

13 REPLIES 13

ChrisBurks
Giga Sage

Hi @nick0989

If you want to keep it all client side and use the Attachment API, the input when used as type file will return a Files array of File objects. You can use the File object with the Attachment API "Uploading a file with a Binary Request".

Here's a basic example:

//Client Script of UI Page

//create an event listener on change
upFile.addEventListener('change', attachFiles, false)

//file handler callback for the event listener
function attachFiles(evt){
        //get the name of the first File object 
	var filename = evt.target.files[0].name;

        //get the content type of the first File object
	var contentType = evt.target.files[0].type;
	
        //Passing the values to the html view
	fname.innerText = "File Name: " + filename;
	type.innerText = "Content Type: " + contentType;
	
        //Using xmlhttp request (you might have to send Creds).. You could also use the REST Message Web Services in ServiceNow alternatively
        //setting up the url with params
	var url = "/api/now/attachment/file?file_name=" + filename + "&table_name=incident&table_sys_id=";

        //I'm just grabbing a task record. You don't have to do this step if you already have the sys_id and know the table
        var incGr = new GlideRecord('incident');
	incGr.setLimit(1);
	incGr.query(function(incGr){
	   if(incGr.next()){
                //adding sysid of record to my params
		url += incGr.sys_id + "";

		var oReq = new XMLHttpRequest();
		oReq.open("POST", url, true);
		oReq.onload = function (oEvent) {
		    // If you reach here it is successfully Uploaded.
                   //just printing out to my html view the details
		   var span = document.createElement('span');
		   span.innerText = "File uploaded successfully to ";
		   var anchor = document.createElement('a');
		   anchor.href = '/incident.do?sys_id=' + incGr.sys_id;
		   anchor.innerText = incGr.number.toString();
		   span.appendChild(anchor);
		   fdata.appendChild(span);
		};
               
               //send the request passing in the one file object
	       oReq.send(evt.target.files[0]);

	   }
        });
}

 

<?xml version="1.0" encoding="utf-8" ?>
<j:jelly trim="false" xmlns:j="jelly:core" xmlns:g="glide" xmlns:j2="null" xmlns:g2="null">
<!-- UI Page HTML -->
	  
	 <form>
		 <label>Upload File</label>
		 <input id="upFile" type="file" />
	</form>
	<section>
		<p id="fname"></p>
		<p id="type"></p>
		<p id="fdata"></p>
	</section>

</j:jelly>

 

If you wanted to send via Server Side, one way to get the file contents from an uploaded file through the input element is using the Web FileReader API: https://developer.mozilla.org/en-US/docs/Web/API/FileReader

Basically in the Event listener instead of sending the file, create an instance of the FileReader and onload of the file grab the contents through the target result. Note: The example below will be plain text but the API has different methods for different types of data.

Using same HTML markup but a slightly modified script to capture the file content.

upFile.addEventListener('change', attachFiles, false)
function attachFiles(evt){
	var filename = evt.target.files[0].name;
	var contentType = evt.target.files[0].type;

        //Create an instance of FileReader
	var fileR = new FileReader();

        //passing values to html view
	fname.innerText = "File Name: " + filename;
	type.innerText = "Content Type: " + contentType;
	
        //add listener on file reader for the load of file content
	fileR.onload = function(e){

                //get file content
		var content = e.target.result;
   
               //pass contents to the HTML view 
		var span = document.createElement('span');
		span.innerText = "File content: \n" + content;
		fdata.appendChild(span);
		
	};
	
	fileR.readAsBinaryString(evt.target.files[0]);
}

 

 

Thanks, i'll give this a shot in the morning.

I was able to try your approach yesterday and I kept trying but I could only upload one file to the record. Not multiple files. Here is what I had set:

var xhr = new XMLHttpRequest();

var url = [];
//files is just the input type="file" with multiple attribute.
for (var i = 0; i < files.length; i++) {
    url.push({ 
        apiEndpoint: "/api/now/attachment/file?file_name=" + files[i].name + "&table_name=" + table + "&table_sys_id=" + record,
        file: files[i]
    })
}

url.forEach(function(item){
    xhr.open("POST", item.apiEndpoint, true);
    xhr.send(item.file);
})

 

I read the attachment API and it says it only works for one file upload. I thought maybe if I could create a loop and store the URL then loop through it, that it would make multiple calls. This does not seem to work though.

 

Any suggestions for attaching multiple images?

That looks like it should work. I've done it many times before. I'll take a look and get back to you.