Use PDIs? Take our 5-minute survey to help shape the PDI roadmap.

Create attachment from HTML file input

Colleen
Tera Expert

I'm trying to build a UI macro in the global scope that creates a db_image record from an image file input, and adds the db_image reference to a list field on the record. I've not managed to get the image content written correctly.

 

Extract of the client-side code:

function addPicture() {
	var fileInput = document.getElementById('picture_upload'); // picture_upload is a file input element
	if (fileInput.value=='') return;

	var file = fileInput.files[0];
	if (file.type.indexOf('image')!=0) return;

	var reader = new FileReader();

	reader.onload = function() { 
		var content = this.result.replace(/data:[^;]+;base64,/i, '');
		var ga = new GlideAjax('UoBImageUtilsAjax');
		ga.addParam('sysparm_name', 'add');
		ga.addParam('sysparm_file_name', file.name);
		ga.addParam('sysparm_file_type', file.type);
		ga.addParam('sysparm_file_content', content	);
		ga.addParam('sysparm_target_table', 'u_learning_spaces_locations');
		ga.addParam('sysparm_target_id', document.getElementById('record_id').value);

		ga.getXMLAnswer(function(answer) { 
			try {
				var result = JSON.parse(answer); 
				if (result.success==true) {
					g_form.addInfoMessage(file.name + ' attached successfully');
					setTimeout(function() { location.reload(true); }, 1000);
				} else {
					g_form.addErrorMessage(result.error);
				}
			} catch (ex) {
				g_form.addErrorMessage('something has gone wrong');
			}
		})		
	};

	reader.onerror = function() { 
		g_form.addErrorMessage('Error reading ' + file.name);				
	};

	reader.readAsDataURL(file);
}

 

Extract of the server-side code

var result = {success: false, error: '', db_image_id: '', attachment_id: ''};

var file_name = (this.getParameter('sysparm_file_name')+''),
	file_type = (this.getParameter('sysparm_file_type')+''),
	file_content = (this.getParameter('syparm_file_content')+'');

var target_table = (this.getParameter('sysparm_target_table')+''),
	target_id = (this.getParameter('sysparm_target_id')+''),
	target_fld = (this.getParameter('sysparm_target_fld') || 'u_pictures')+'';

try {
var db_image = new GlideRecord('db_image');
db_image.setValue('name', file_name);
db_image.setValue('category', 'general');
db_image.setValue('active', true);
result.db_image_id = db_image.insert();

// GlideSysAttachment.writeBase64 does not work in global scope
var raw = GlideStringUtil.base64DecodeAsBytes(file_content)+'';
result.attachment_id = (new GlideSysAttachment()).write(db_image, file_name, file_type, raw);

if (!result.attachment_id) {
	throw new Error('Error creating attachment record');
}

db_image.setValue('image', result.attachment_id);
db_image.setValue('format', file_type.split('/').pop());
db_image.update();

var target = new GlideRecord(target_table);
if (target.get(target_id)) {
	var pictures = (target.getValue(target_fld)||'').split(',');
	pictures.push(result.db_image_id);
	target.setValue(target_fld, pictures.join(','));
	target.update();
} else {
	throw new Error('Invalid target record');
}

result.success = true;
} catch (ex) {}

 

How do I process the file input so that the image content is saved correctly?

1 ACCEPTED SOLUTION

@Colleen 

update as this

Client side

-> spelling mistake in this.getParameter() is corrected

function addPicture() {
    var fileInput = document.getElementById('picture_upload');

    if (!fileInput || !fileInput.files || fileInput.files.length === 0) {
        g_form.addErrorMessage('Please select an image.');
        return;
    }

    var file = fileInput.files[0];

    if (!file.type || file.type.indexOf('image/') !== 0) {
        g_form.addErrorMessage('Only image files are allowed.');
        return;
    }

    var reader = new FileReader();

    reader.onload = function(event) {
        var dataUrl = event.target.result || '';
        var commaIndex = dataUrl.indexOf(',');

        if (commaIndex === -1) {
            g_form.addErrorMessage('The selected file could not be encoded.');
            return;
        }

        // Remove: data:image/png;base64,
        var base64Content = dataUrl.substring(commaIndex + 1);

        var ga = new GlideAjax('UoBImageUtilsAjax');
        ga.addParam('sysparm_name', 'add');
        ga.addParam('sysparm_file_name', file.name);
        ga.addParam('sysparm_file_type', file.type);
        ga.addParam('sysparm_file_content', base64Content);
        ga.addParam('sysparm_target_table', 'u_learning_spaces_locations');
        ga.addParam(
            'sysparm_target_id',
            document.getElementById('record_id').value
        );
        ga.addParam('sysparm_target_field', 'u_pictures');

        ga.getXMLAnswer(function(answer) {
            try {
                var result = JSON.parse(answer);

                if (result.success === true) {
                    g_form.addInfoMessage(
                        file.name + ' attached successfully'
                    );

                    setTimeout(function() {
                        window.location.reload(true);
                    }, 1000);
                } else {
                    g_form.addErrorMessage(
                        result.error || 'The image could not be saved.'
                    );
                }
            } catch (ex) {
                g_form.addErrorMessage(
                    'Invalid response received from the server.'
                );
            }
        });
    };

    reader.onerror = function() {
        g_form.addErrorMessage(
            'Error reading ' + file.name
        );
    };

    reader.readAsDataURL(file);
}

Server script -> use GlideStringUtil.base64DecodeAsBytes() followed by the global GlideSysAttachment.write()

var UoBImageUtilsAjax = Class.create();
UoBImageUtilsAjax.prototype = Object.extendsObject(AbstractAjaxProcessor, {

    add: function() {
        var result = {
            success: false,
            error: '',
            db_image_id: '',
            attachment_id: ''
        };

        try {
            var fileName = this.getParameter('sysparm_file_name') || '';
            var fileType = this.getParameter('sysparm_file_type') || '';
            var fileContent =
                this.getParameter('sysparm_file_content') || '';

            var targetTable =
                this.getParameter('sysparm_target_table') || '';
            var targetId =
                this.getParameter('sysparm_target_id') || '';
            var targetField =
                this.getParameter('sysparm_target_field') ||
                'u_pictures';

            if (!fileName || !fileType || !fileContent) {
                throw new Error('File name, type, or content is missing.');
            }

            if (fileType.indexOf('image/') !== 0) {
                throw new Error('Only image files are allowed.');
            }

            if (!targetTable || !targetId || !targetField) {
                throw new Error('Target record information is incomplete.');
            }

            var target = new GlideRecord(targetTable);

            if (!target.isValid()) {
                throw new Error('Invalid target table.');
            }

            if (!target.get(targetId)) {
                throw new Error('Target record was not found.');
            }

            if (!target.isValidField(targetField)) {
                throw new Error(
                    'Invalid target field: ' + targetField
                );
            }

            // Validate the payload before decoding.
            fileContent = fileContent
                .replace(/\s/g, '')
                .replace(/-/g, '+')
                .replace(/_/g, '/');

            if (!/^[A-Za-z0-9+/]*={0,2}$/.test(fileContent)) {
                throw new Error('Invalid Base64 image content.');
            }

            // Decode Base64 into binary bytes.
            var bytes =
                GlideStringUtil.base64DecodeAsBytes(fileContent);

            if (!bytes || bytes.length === 0) {
                throw new Error('The decoded image content is empty.');
            }

            /*
             * Create the db_image record first.
             */
            var dbImage = new GlideRecord('db_image');
            dbImage.initialize();
            dbImage.setValue('name', fileName);
            dbImage.setValue('category', 'general');
            dbImage.setValue('active', true);

            var dbImageId = dbImage.insert();

            if (!dbImageId) {
                throw new Error('Could not create db_image record.');
            }

            result.db_image_id = dbImageId;

            /*
             * Attach the decoded bytes to the db_image record.
             */
            var attachment = new GlideSysAttachment();

            var attachmentId = attachment.write(
                dbImage,
                fileName,
                fileType,
                bytes
            );

            if (!attachmentId) {
                throw new Error(
                    'Could not create attachment for db_image.'
                );
            }

            result.attachment_id = attachmentId;

            /*
             * The db_image.image field stores the attachment sys_id.
             */
            dbImage.setValue('image', attachmentId);
            dbImage.setValue(
                'format',
                fileType.substring(fileType.indexOf('/') + 1)
            );
            dbImage.update();

            /*
             * Add the db_image sys_id to the list field.
             */
            var existingValue =
                target.getValue(targetField) || '';

            var imageIds = existingValue
                .split(',')
                .filter(function(id) {
                    return id;
                });

            if (imageIds.indexOf(dbImageId) === -1) {
                imageIds.push(dbImageId);
            }

            target.setValue(targetField, imageIds.join(','));
            target.update();

            result.success = true;
        } catch (ex) {
            result.error = ex.message || String(ex);

            gs.error(
                'UoBImageUtilsAjax.add failed: ' + result.error
            );
        }

        return JSON.stringify(result);
    },

    type: 'UoBImageUtilsAjax'
});

💡 If my response helped, please mark it as correct and close the thread 🔒— this helps future readers find the solution faster! 🙏

Regards,
Ankur
Certified Technical Architect  ||  10x ServiceNow MVP  ||  ServiceNow Community Leader

View solution in original post

8 REPLIES 8

Hi Ankur

 

I understand what you were trying to tell me. There was a typo in the server script.

 

file_content = (this.getParameter('syparm_file_content')+'')
 
rather than
 
file_content = (this.getParameter('sysparm_file_content')+'')
 
When I fixed the typo, the image upload worked.

@Colleen 

yes that's what I mentioned the spelling mistake

Glad that my script worked.

💡 If my response helped, please mark it as correct and close the thread 🔒— this helps future readers find the solution faster! 🙏

Regards,
Ankur
Certified Technical Architect  ||  10x ServiceNow MVP  ||  ServiceNow Community Leader

It was actually your script that confused me, since it was essentially the same as what I had written. It would have been a lot clearer if you had simply pointed out the typo.

@Colleen 

Thanks for the feedback.

I did mention the correction but may be it got missed due to the script section

AnkurBawiskar_0-1787215042865.png

 

Regards,
Ankur
Certified Technical Architect  ||  10x ServiceNow MVP  ||  ServiceNow Community Leader