- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-03-2016 02:21 PM
I want to upload a PDF somehow to my instance and then call that PDF as a link to be viewed and/or downloaded. I have a UI page setup, but can't figure out how to upload a PDF and then call that PDF in my code. HELP!!!
Solved! Go to Solution.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎02-04-2016 06:38 AM
Charlie, so I did some looking online and it looks from these strings that truly public kb isn't possible:

- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎07-04-2018 03:23 AM
What I know on this so far is, you actually can't do a knowledgebase enabled for public view at all, but you're able to create a preview of the file with the download direct link. As far as I know, it's possible to do so with this PDF editing tool https://w9.pdffiller.com it's more about filling and editing forms, here is an example on how it works with the W-9 tax form, but I see it will fit for your purposes as well
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
‎08-16-2024 11:56 AM
A UI Page with the following HTML:
<html>
<head>
<title>Upload PDF</title>
</head>
<body>
<h2>Handle PDF Attachments Table</h2>
<input type="file" id="pdfFile" accept="application/pdf" />
<button onclick="uploadPDF()">Upload</button>
<button onclick="downloadPDF()">Download</button>
<button onclick="canIconnect()">Try it</button>
<br/>
<div id="filename"></div>
<br/>
<div id="filetype"></div>
<br/>
<div id="result"></div>
<br/>
<div id="message"></div>
</body>
</html>
The client script piece to upload is:
function uploadPDF() {
var fileInput = document.getElementById('pdfFile');
var file = fileInput.files[0];
if (!file) {
alert('Please select a PDF file to upload.');
return;
}
try {
document.getElementById('filename').innerText = 'PDF file name is ' + file.name;
var reader = new FileReader();
// The placement of reader.readAsDataURL(file); after reader.onload
// in the code is intentional and necessary for the correct functioning
// of the FileReader API.
reader.onload = function(event) {
// event.target.result as if you opened the PDF with notepad
var base64Content = event.target.result.split(',')[1]; // Get Base64 content without the prefix
var tableName = 'sys_ui_page'; // Set this to the table where the attachment should be uploaded
var recordSysId = '0e9b89f41b049e1076f215ff034bcb7f'; // Set this to the sys_id of the record
// Send the raw data (Base64) to the server
var ga = new GlideAjax('global.AAIfileUploadUtilityGlobal');
ga.addParam('sysparm_name', 'uploadPDFglobal');
ga.addParam('tableName', tableName);
ga.addParam('recordSysId', recordSysId);
ga.addParam('fileName', file.name);
document.getElementById('filetype').innerText = 'MIME type is '+file.type;
ga.addParam('filetype', file.type);
ga.addParam('base64Content', base64Content);
document.getElementById('filename').innerText = 'base64Content is ' + base64Content.slice(0,20);
ga.getXMLAnswer(_handleUploadResponse);
};
// Once the file has been read, the onload event is triggered, and the code
// within the onload function executes.
/*
When reader.readAsDataURL(file); is called, the FileReader starts reading
the file as a Data URL. Once the reading is complete, the onload event is
triggered, and the event object inside the onload function contains the
Base64-encoded file content, as well as a reference to the original File
object that was read.
*/
reader.readAsDataURL(file); // Read the file as a Data URL (Base64 encoded)
}
catch (error){
document.getElementById('result').innerText = 'hummph ...' + error.message;
}
}
function _handleUploadResponse(response) {
if(response){
var result = JSON.parse(response);
if(result){
if(result.recordSysId !== '0') {
document.getElementById('result').innerText =
'Attachment uploaded to table: ' + result.tableName +
'\nRecord sys_id: ' + result.recordSysId +
'\nAttachment sys_id: ' + result.attachmentSysId;
} else {
document.getElementById('result').innerText = 'script include problem: ' + result.tableName;
}
} else {
document.getElementById('result').innerText = 'hummph ... no result' ;
}
}
else {
document.getElementById('result').innerText = 'hummph ... no response' ;
}
}
The client script to download is this:
function downloadPDF() {
try{
var fileInput = document.getElementById('pdfFile');
var file = fileInput.files[0];
if (!file) {
alert('Please select a PDF file to upload.');
return;
}
else {
alert('You selected this file: ' + file.name);
}
var tableName = 'sys_ui_page'; // Set this to the table where the attachment should be uploaded
var recordSysId = '0e9b89f41b049e1076f215ff034bcb7f'; // Set this to the sys_id of the record
var ga = new GlideAjax('global.AAIfileUploadUtilityGlobal');
ga.addParam('sysparm_name', 'readAttIntoString');
ga.addParam('tableName', tableName);
ga.addParam('fileName', file.name);
ga.addParam('recordSysId', recordSysId);
document.getElementById('result').innerText = 'download ga parms loaded for ' + file.name;
ga.getXMLAnswer(_handleDownloadResponse);
}
catch(error){
document.getElementById('result').innerText = 'hummph in download ...' + error.message;
}
}
function convertToCSV(dataAsStringFromScriptInclude) {
try{
var itemLen = 4;
var resultArray = [];
// Loop through the string in steps of itemLen characters
for (var i = 0; i < dataAsStringFromScriptInclude.length; i += itemLen) {
// Get the next itemLen characters
var chunk = dataAsStringFromScriptInclude.substr(i, itemLen);
resultArray.push(chunk);
}
// Join the array into a CSV string
var csvString = resultArray.join(',');
return csvString;
}
catch(error){
return error.message + ' from convertToCSV';
}
}
function _handleDownloadResponse(response) {
if(response !== undefined){
var dataFromScriptInclude = JSON.parse(response);
document.getElementById('message').innerText = 'response: ' + dataFromScriptInclude.slice(0,20);
var dataAsStringFromScriptInclude = dataFromScriptInclude.toString();
if(dataAsStringFromScriptInclude){
try {
// // Ensure the string is correctly padded
// while (dataAsStringFromScriptInclude.length % 4 !== 0) {
// dataAsStringFromScriptInclude += "=";
// }
var stringArray = '';
var binaryArray = [];
if (dataAsStringFromScriptInclude.includes(',')) {
document.getElementById('message').innerText = 'response: ' + dataAsStringFromScriptInclude.slice(0,20) ;
// Step 1: Split the CSV string into an array of strings
if (dataAsStringFromScriptInclude && typeof dataAsStringFromScriptInclude === 'string') {
stringArray = dataAsStringFromScriptInclude.split(',');
} else {
// Handle the case where dataAsStringFromScriptInclude is not a valid string
var str = dataAsStringFromScriptInclude.toString();
document.getElementById('message').innerText = 'str is ' + str.slice(0,20);
stringArray = str.split(',');
}
// Step 2: Convert each string to an integer
var intArray = stringArray.map(function(num) {
return parseInt(num, 10);
});
// Step 3: Create a Uint8Array from the integer array
binaryArray = new Uint8Array(intArray);
}
else {
document.getElementById('message').innerText = 'response has no commas';
while (dataAsStringFromScriptInclude.length % 4 !== 0) {
dataAsStringFromScriptInclude += "=";
}
var len = dataAsStringFromScriptInclude.length;
var binaryData = new Uint8Array(len);
for (var i = 0; i < len; i++) {
binaryData[i] = dataAsStringFromScriptInclude.charCodeAt(i);
}
}
var blob = new Blob([binaryArray], { type: 'application/pdf' });
// document.getElementById('message').innerText = 'blob is set';
// Step 4: Create a download link and trigger the download
var link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'file.pdf';
link.click();
// document.getElementById('message').innerText = 'link was clicked';
// Optionally, revoke the object URL after the download
window.URL.revokeObjectURL(link.href);
}
catch(error){
document.getElementById('result').innerText = 'hummph in download handler ...' + error.message;
}
} else {
document.getElementById('result').innerText = 'hummph in download handler ... no base64String' ;
}
}
else {
document.getElementById('result').innerText = 'hummph in download handler ... no download response' ;
}
}
And the biggest trick is you need a Script Include because of scoping and how it handles Glide objects:
var AAIfileUploadUtilityGlobal = Class.create();
AAIfileUploadUtilityGlobal.prototype = Object.extendsObject(global.AbstractAjaxProcessor, {
// initialize: function() {},
canIconnect: function() {
/*
you may have noticed that we don't return any values from our function. We can if we
want, but it is not necessary. The AbstractAjaxProcessor class (which is our
superclass - which means we're extending it) has built-in functionality that returns
the entire XML document when we're through.
*/
var attrib = this.getParameter('aString');
var strResponse = 'correct response from AAIfileUploadUtilityGlobal canIconnect ' + attrib;
return JSON.stringify(strResponse);
//return strResponse;
},
readAttIntoString: function() {
var tableName = this.getParameter('tableName');
var recordSysId = this.getParameter('recordSysId'); //sys_id of the record that contains attachment
var fileName = this.getParameter('fileName');
var attGr = new GlideRecord('sys_attachment'); //go to sys_attachment table
attGr.addQuery('table_name', tableName); //specify your table name as second parameter
attGr.addQuery('table_sys_id', recordSysId); // sys_id of the row associated with the attachment you're trying to get
attGr.addQuery('file_name', fileName); // full name and extension of the attachment
attGr.orderByDesc('sys_updated_on'); //helps to retrieve the latest attachment to record
attGr.query();
if(attGr.next())
{
var gsa = new global.GlideSysAttachment();
var attachmentData = gsa.getBytes(attGr);
return JSON.stringify(attachmentData);
}
},
uploadPDFglobal: function() {
var result = {};
var step = 'after try start';
try {
var tableName = this.getParameter('tableName');
var recordSysId = this.getParameter('recordSysId');
var fileName = this.getParameter('fileName');
var filetype = this.getParameter('filetype');
var base64Content = this.getParameter('base64Content');
// a long string of encoded binary, Base64 content without the prefix
if(!tableName) {throw new Error('tableName not sent');}
if(!recordSysId) {throw new Error('recordSysId not sent');}
if(!fileName) {throw new Error('fileName not sent');}
if(!filetype) {throw new Error('filetype not sent');}
if(!base64Content) {throw new Error('base64Content not sent');}
result = {
attachmentSysId: 'attachmentSysId',
tableName: tableName,
recordSysId: recordSysId
};
if(true){
step = 'before base64Encode';
// Base64 content is passed directly; assume it's stored or handled
// by another system
// GlideStringUtil.base64Encode works only in global scope
var decodedBase64FileConent = GlideStringUtil.base64DecodeAsBytes(base64Content); // this step is crucial
step = 'find the UI Page record';
var rec = new GlideRecord(tableName);
rec.get(recordSysId);
step = 'Check if the record was found';
if (rec.isValidRecord()) {
var attachment = new global.GlideSysAttachment();
// There is a scoping error in this version of SN where the GlideSysAttachment API.
// writeBase64(GlideRecord now_GR, String fileName, String contentType, String content_base64Encoded)
step = 'try to attach with writeBase64';
var attachmentSysId = attachment.write(rec, fileName, filetype, decodedBase64FileConent);
// writeBase64 is only for scoped applications
// unfortunately, GlideStringUtil is only available in the global scope, so I made this script include global
step = 'after writeBase64';
if(attachmentSysId){
result = {
attachmentSysId: attachmentSysId,
tableName: tableName,
recordSysId: recordSysId
};
}
else {
result = {
attachmentSysId: '-1212',
tableName: 'attachmentSysId has no value',
recordSysId: '0'
};
}
} else {
result = {
attachmentSysId: '-2758',
tableName: 'invalid sys_id for tablename of ' + tableName,
recordSysId: recordSysId
};
}
}
} catch (error) {
result = {
attachmentSysId: '0',
tableName: 'Error: ' + error.message + ' at ' + step,
recordSysId: '0'
};
}
// Return the result as a JSON string
return JSON.stringify(result);
},
type: 'AAIfileUploadUtilityGlobal'
});