How to download all attachments from a record to a Zip file
Hi all,
I've seen this question asked a lot on the forums in various ways and majority of the responses point to either an old Guru (3rd party website) article that's a bit outdated or the response involves some sort of complex solution that majority of the time...is not needed.
I do want to preface this article with this callout....the solution I'm about to define is already available on your instance. This means you don't have to install anything.
So where is this "Download All" feature already available within your instance? Out of box, the "Download All" button, is located within the attachments menu (accessible by the paperclip icon for example) and is used to download all of the attachments associated to that current record as a Zip file:
So...that's it? Article over?
If the above satisfies your need and you don't need anything else, then yes, thanks for reading and enjoy the platform! If however...you were wanting to do something else...like make this feature more accessible or call it out a bit more or perhaps you have attachments on ANOTHER record that isn't the current record (perhaps a child record) and you want to download them by calling that action here (on the current record)…instead?
There's a few use cases as mentioned above that I'll go through and explain.
Use case #1: You want to create a more accessible experience that allows you to download all attachments for the current record
In this scenario, let's assume your audience doesn't necessarily like clicking the attachment icon to then click "Download All" and you want to make their life easier by creating a form button or form link, etc. where they can just click it and voila...all the attachments download that are associated to the current record into a nice Zip file.
This can be accomplished by creating a UI Action!
- Using the Incident form as an example, let's access the contextual menu and go to Configure > UI Action
- From here, we'll name our UI Action and ensure it's set Active = true
- You can decide if you want this to show up on insert (when creating new record) or update (after the record has been created) -- normally I'd say just check "Show update"
- Make sure "Client" = true
- Make your selection if you want this as a Form button, Form context menu, Form link, etc.
- The OnClick field should be set to a function name such as: openURL()
- If you want this UI Action to be conditional, utilize the Condition field as needed
- Within the script section, you can use something as simple as this:
function openURL() { g_navigation.openPopup("/download_all_attachments.do?sysparm_sys_id=" + g_form.getUniqueValue()); } - If you want this to appear within Agent Workspace, then scroll down and check the box for "Workspace Form Button" and then use script such as:
function onClick(g_form) { top.window.open("/download_all_attachments.do?sysparm_sys_id=" + g_form.getUniqueValue()); }
Use case #2: You want to utilize the "download all attachments" feature on the current record, but the attachments are on another record
The setup for this use case is essentially the same as Use case #1 (same steps 1-7), except the script (steps 8 and 9) would refer to a specific record other than the current record. The easiest method would be if your current record had a field on it that references the target record that holds all the attachments, if so, your script would simply be:
function openURL() {
g_navigation.openPopup("/download_all_attachments.do?sysparm_sys_id=" + g_form.getValue('reference_field_name');
}
And for Agent Workspace, you'd use:
function onClick(g_form) {
top.window.open("/download_all_attachments.do?sysparm_sys_id=" + g_form.getValue('reference_field_name');
}
If perhaps the record that contains all the attachments is not on the current record as a reference field, for example...you may need to conduct a GlideRecord query based on some values on the current record to find the target record, etc. then you'd want to utilize GlideAjax in your UI Action. Example script could be:
UI Action:
function openURL() {
var ga = new GlideAjax('urlRecordQuery');
ga.addParam('sysparm_name', 'conductQuery');
ga.addParam('sysparm_field_value', g_form.getValue('field_name'));
ga.getXMLWait();
var sysID = ga.getAnswer();
if (sysID) {
g_navigation.openPopup("/download_all_attachments.do?sysparm_sys_id=" + sysID);
} else {
g_form.addErrorMessage("No valid record was retrieved");
}
}
UI Action for Agent Workspace:
function onClick(g_form) {
var ga = new GlideAjax('urlRecordQuery');
ga.addParam('sysparm_name', 'conductQuery');
ga.addParam('sysparm_field_value', g_form.getValue('field_name'));
ga.getXMLWait();
var sysID = ga.getAnswer();
if (sysID) {
top.window.open("/download_all_attachments.do?sysparm_sys_id=" + sysID);
} else {
g_form.addErrorMessage("No valid record was retrieved");
}
}
Script Include with Client Callable checkbox = true
var urlRecordQuery = Class.create();
urlRecordQuery.prototype = Object.extendsObject(AbstractAjaxProcessor, {
conductQuery: function(){
var fieldValue = this.getParameter('sysparm_field_value');
var sysID;
var gr = new GlideRecord('table_name');
gr.addQuery('field_name', fieldValue);
gr.query();
if (gr.next()) {
sysID = gr.sys_id;
} else {
sysID = '';
}
return sysID;
},
type: 'urlRecordQuery'
});
The above are just examples, but hopefully you get the idea. You can add more parameters to your GlideAjax and Script Include as needed to help supply your GlideRecord query with more query parms.
Note: You can also accomplish the above with using both Client and Server side code in your UI Action, but for the sake of this article, we are using client side scripting and GlideAjax for any server calls we need to make.
Use case #3: You want to utilize the "download all attachments" feature on the current record, but the attachments are on multiple different records
This solution is the most complex out of the three and requires you to do a combination of Use case #1 (steps 1-7) and Use case #2 (use GlideAjax to communicate with the server to do 'x').
The overall idea here is that within your script include you'd leverage the GlideSysAttachment API to temporarily make copies of the attachments from those target records and house them on the current record (or a blank new record or somewhere else), once complete, you'd fire an event that would trigger a script action all while you'd return the appropriate record sys_id that houses your attachments to your UI Action to then commence the download. The download process executed by your UI Action runs very quick, while the script action takes a few seconds before it actually starts to circle back through and delete the attachments from the housing record. Again, this is all an example to get your mind thinking how you'd like to accomplish this.
https://www.servicenow.com/community/developer-articles/how-to-download-all-attachments-from-a-record-to-a-zip-file/ta-p/2322589