Travis Toulson
Administrator
Administrator

Working with Image Fields and Attachments.png

Check out other articles in the C3 Series

 

If you've ever tried to work with images in ServiceNow beyond simple document attachments, you've probably discovered that the platform's attachment system has some unique characteristics. When we built CreatorCon C3 for Knowledge 2025, we processed around 130,000 images through ServiceNow's attachment system including user uploaded photos, AI generated avatars, and various card assets.

 

This experience taught us valuable lessons about ServiceNow's image handling capabilities, performance limitations, and when you should consider alternative approaches. Whether you're building a simple image gallery, a complex image processing pipeline, or just trying to shuffle around a bunch of binary file attachments understanding these constraints will save you significant time and frustration.

 

The ServiceNow Attachment Reality Check

 

Attachment LifecycleAttachment Lifecycle

 

Let's start with the fundamental reality: ServiceNow wasn't designed to be a file server. The platform's attachment system works well for typical business documents and small images, but it has architectural characteristics that create a number of challenges when working with large volumes of image data or other attachments. To better understand the issues, let's first take a look at how ServiceNow handles attachments.

 

Here's what happens when you store a file in ServiceNow:

 

  • Binary file gets base64 encoded
  • Base64 data gets split into chunks of a fixed size
  • Record created in sys_attachment table
  • Each chunk becomes a record in sys_attachment_doc table with ordering

 

To retrieve an attachment, the process is reversed:

 

  • Query the sys_attachment
  • Query the related sys_attachment_doc chunks in order
  • Concatenate the chunks together
  • Decode the base64 back into binary

 

This approach ensures data integrity and works within ServiceNow's database-centric architecture. But all that string encoding and decoding comes at a performance cost that become significant at larger volumes.

 

The Image Field Gotcha: Two APIs Required

 

Attachments Fields must save the attachment and the field valueAttachments Fields must save the attachment and the field value

 

The biggest surprise when working with ServiceNow image fields is that you can't set them directly like other fields. Here's what you might expect to work but doesn't:

 

// This DOESN'T work for image fields
var gr = new GlideRecord('my_table');
gr.get(sysId);
gr.image_field = base64ImageData; // Nope!
gr.update();

 

Instead, image fields store a sys_id reference to an attachment record. To set an image field, you need two separate operations:

 

Step 1: Create the Attachment

 

// Create the attachment first
var attachment = new GlideSysAttachment();
var attachmentId = attachment.write(
    gr,                    // GlideRecord object
    fieldName,              // Setting file name to the name of the image field links it to the field
    contentType,           // MIME type
    content            // Base64 Encoded string of file contents
);

 

Step 2: Set the Field Reference

 

// Then set the image field to reference the attachment
gr.image_field = attachmentId;
gr.update();

 

Updating Images: Delete, Create, Update

 

Updating an existing image is even more complex because you can't update attachments in place:

 

function updateImageField(gr, fieldName, inputStream, fileName, contentType) {
    // Step 1: Delete existing attachment if it exists
    var existingAttachmentId = gr.getValue(fieldName);
    if (existingAttachmentId) {
        var existingAttachment = new GlideSysAttachment();
        existingAttachment.deleteAttachment(existingAttachmentId);
    }
    
    // Step 2: Create new attachment
    var attachment = new GlideSysAttachment();
    var newAttachmentId = attachment.writeContentStream(
        gr, fileName, contentType, inputStream
    );
    
    // Step 3: Update the field reference
    gr.setValue(fieldName, newAttachmentId);
    gr.update();
}

 

Script Include: Abstracting the Complexity

 

To manage this complexity across multiple image fields in our C3 application, I created a script include that handled the create/update/delete operations:

 

var ImageField = Class.create();
ImageField.prototype = {
    initialize: function() {
    },

	/**
	 * Set the value of an image field
	 *  {GlideRecord} record - Record which has an image field
	 *  {String} fieldName - Name of the image field on the record
	 *  {Object} photo - Object containing the image data
	 *  {String} photo.data - Base64 encoded representation of the image file
	 *  {String} photo.mimeType - HTTP Mime type of the file such as image/jpg
	 */
	setValue(record, fieldName, photo) {
		let gsa = new GlideSysAttachment();

		if (!record[fieldName].nil()) {
			gsa.deleteAttachment(record[fieldName].toString());
		}

		let attachmentId = gsa.writeBase64(record, fieldName, photo.mimeType, photo.data);

		record.setValue(fieldName, attachmentId);
		record.update();
	},

	getAttachment(record, fieldName) {
		let gsa = new GlideSysAttachment();

		let attachment = new GlideRecord('sys_attachment');

		if(attachment.get(record[fieldName].toString())) {
			return { 
				data: gsa.getContentBase64(attachment),
				mimeType: attachment.content_type.toString(),
			};
		}
		else  {
			return null;
		}
	},

    type: 'ImageField'
};

 

This abstraction made it much easier to work with our multiple image fields across profile records, photo submissions, and generated images.

 

Performance Reality: File System vs ServiceNow

 

Filesystem only stores binary while ServiceNow must convert to Base64Filesystem only stores binary while ServiceNow must convert to Base64

 

The performance difference between traditional file systems and ServiceNow's attachment system becomes stark at scale.

 

In one example, we had to download a 4GB zip folder of images from a cloud provider, unzip it, and then upload into ServiceNow image fields. Important to note that my network has symmetrical 1000GB upload / download. Obviously, we would expect some performance difference just from the fact that the download was compressed and the upload was not, but the differences were stark... and not just in terms of over the wire performance.

 

Filesystem Based Images

 

  • Downloaded and unzipped in minutes
  • Direct binary transfer, so no base64 overhead
  • Standard file system operations made working with the files easier
    • Drag and drop
    • File search
    • Folders
    • This stuff should not be taken for granted

 

ServiceNow Based Image Attachments

 

  • Upload to ServiceNow took several hours
    • Each file required base64 encoding before it could be stored
    • Multiple database inserts per file (chunking)
    • REST API overhead for each operation
  • Required writing custom scripts to handle the upload
  • If a mistake happened
    • File operations are much more complicated
    • No folders
    • Can't see the images when you search attachments
    • Deleting for attachment fields is a pain

The math and the complications are sobering. When you're processing thousands of images, ServiceNow's attachment system can become a significant bottleneck.

 

Image Fields: The Good Parts

 

On the other hand, there are some pretty significant upsides to leveraging Image Fields and ServiceNow attachments. 

 

Native platform API's

 

Despite having to use two different API's to coordinate Image Fields... the API's do exist. In fact, Attachments have a robust set of JavaScript API's and REST API's which allowed us to fill in the gaps as needed. If we had chosen instead to use an external provider, then we would have had to work with a spoke or some other integration piece.

 

Built in Access Control

 

Built in access control is one of my top reasons for sticking with attachments. As the app grows, we are adding roles for moderators, kiosks, and likely external vendors. It's really comforting to not have to stress about what might be accessible... see the record, see the attachment.

 

List / Form View Visibility

 

One great thing about Image fields is that the image is visible in both form and list views without needing any pesky custom formatters. This is particularly helpful for both moderation and tech support purposes on the conference floor. 

 

Licensing

 

This one is likely the number one reason we will stick with attachments. We already have a ServiceNow instance and it allows us to store files. No pricing additional cloud services. No additional security reviews. Just pure ServiceNow goodness.

 

User Experience Lessons

 

In addition to performance and infrastructure related considerations, we also had a few lessons learned on the user experience side of things related to images.

 

Progressive Optimization Needed

 

 

We eventually realized that we needed to optimize the final rendered card image for the mobile app but by that point it was too late to rework the intake app. The result was a lot of people on conference wifi trying to download multiple MB sized images at a time. Needless to say, this had it's issues.

 

An unexpected side effect of this was when we had the full resolution images visible in the list view and set our list to show 100 records at a time... let's just say we couldn't use that laptop to troubleshoot on the conference floor for a minute.

 

Each of these issues could have been resolved if we stored a low res version of key images that would be visible to end users. This concept may even impact our data model for storing images.

 

Better Progress Indicators

 

Users often thought the system had broken when it was just taking time. We would see the avatar generated on the internal app but between all the moving images, record watchers, and everything else going on in the app, sometimes it would take longer for the images to propagate to the Service Portal intake app. In fact, there were times where the record watcher seemed to fail all together and the only solution was to refresh the intake portal. We definitely would have benefited from better progress indicators than a simple loading spinner, especially when it could take a bit for the images themselves to load.

 

Plan for Failures

 

Moving images in volume is a big task and there are bound to be failures. Fortunately, we built in many layers of handling the unexpected. We added a custom logging mechanism to track what was happening on a per task basis. Our image handling processes had pretty straightforward ways to retry different steps of the process. And we had dashboards and methods for proactively monitoring for failures. And honestly, a lot of the retry capabilities were a happy accident. The async way we implemented the processes for performance reasons had a fun side effect of making almost everything easy to retry as well.

 

Key Takeaways

 

Working with 130,000 images in ServiceNow taught us several crucial lessons:

 

 

  1. ServiceNow's attachment system works well for its intended use - business documents and small files
  2. Image field complexity requires abstraction - Build helper functions to manage the two-API requirement
  3. Performance degrades significantly at scale - Plan for slower operations with large image volumes
  4. Error handling is critical - Build comprehensive monitoring and manual remediation processes
  5. External storage deserves consideration - For large-scale image processing, hybrid architectures may be optimal
  6. User experience requires optimization - Image size and processing time directly impact user satisfaction

 

The CreatorCon C3 project pushed ServiceNow's attachment system to its limits. While we successfully delivered an amazing user experience, the lessons learned will definitely influence our architectural decisions for future image-heavy applications.

 

Understanding these constraints doesn't mean avoiding images in ServiceNow... on the contrary, we will probably stick with it. It just means making informed decisions about when to use platform capabilities and when to consider alternatives.

 

1 Comment