TUESDAY, FEBRUARY 07, 2012

Download Attachments as a ZIP File

I

am really enjoying being here at Knowledge 10 and meeting so many of you who read our posts.  I have been busy in one-on-one sessions hearing requests and helping come up with answers.  One of our readers wanted to know how they would go about downloading all of the attachments on a record as a zip file.  Another reader wanted to know how to send attachments as a zip file via a web service.  I believe this post should help solve both questions.

The challenge we will solve in this post is this: if a record has any attachments, we will add a button to the form that allows you to download all of the attachments as a single ZIP file.

Step 1: Create a UI action on the task table

Name:Save Attachments as ZIP
Condition:

current.hasAttachments();

Script:

current.update();
action.setRedirectURL('exportAttachmentsToZip.do?sysparm_sys_id=' +  current.sys_id + '&sysparm_table='  + current.getTableName());

The only real secret to the above code is the setRedirectURL. This allows us to call a processor in our system that will popup a download dialog to download the zip file. The exportAttachmentsToZip processor does not yet exist in the system and will need to be created. Processors are found under System Definition->Processors.

Step 2: Create an exportAttachmentsToZip processor
Create a new processor. Processors are found under System Definition->Processors.

Name: exportAttachmentsToZip
Type: script
Path: exportAttachmentsToZip
Script:

var sysid = g_request.getParameter('sysparm_sys_id');
var table = g_request.getParameter('sysparm_table');

var theRecord = new GlideRecord(table);
theRecord.addQuery('sys_id', sysid);
theRecord.query();
theRecord.next();

var zipName = 'attachments.zip';

var StringUtil = Packages.com.glide.util.StringUtil;

var gr = new GlideRecord('sys_attachment');
gr.addQuery('table_sys_id', theRecord.sys_id);
gr.addQuery('table_name', theRecord.getTableName());
gr.query();

gs.log('sys_id: ' + theRecord.sys_id);
gs.log('table: ' + theRecord.getTableName());

if (gr.hasNext()){
   g_response.setHeader('Pragma', 'public');
   g_response.addHeader('Cache-Control', 'max-age=0');
   g_response.setContentType('application/octet-stream');
   g_response.addHeader('Content-Disposition', 'attachment;filename=' + zipName);
   var out = new Packages.java.util.zip.ZipOutputStream(g_response.getOutputStream());
   var count=0;
   while (gr.next()){
      var sa = new Packages.com.glide.ui.SysAttachment();
      var binData = sa.getBytes(gr);

      var file = gr.file_name;
      this.addBytesToZip(out, zipName, file, binData);
      gs.log('Adding ' + file + ' to ' + zipName);
      count ++;
   }
   gs.log('Added ' + count + ' files');
   // Complete the ZIP file
   out.close();
}

function addBytesToZip (out, dir, file, stream){
   // Add ZIP entry to output stream.
   out.putNextEntry(new Packages.java.util.zip.ZipEntry(file));
   out.write(stream, 0, stream.length);
   out.closeEntry();
}

That’s all there is to it. The result will appear like the following screen shot.

This code could be slightly tweaked to send the ZIP file as a web service. Remember, though, that if you do this, you will need to base64 encode the binary data before sending it over.

Comments

Posted On
Apr 24, 2010
Posted By
ElPunto

Jacob, thanks for the article.

I tried the code but there seems to be aproblem with the zip format. It’s not recognized by pkzip and other zip tools. One of the differences is that it has set the general purpose bit 3 (0×0008)(crc,compressed/uncompressed zero in header) and pkzip uses bit 1 (0×0002). Compression method is both 8 (0×0008) but that should not be a problem.

Posted On
Apr 25, 2010
Posted By
ElPunto

Forget the previous remark,

It works OK. Because I used the Java Script Editor plugin it messed up the layout.

The final close was missing and that caused an incomplete dictionary entry. I will now try and integrate it in my Webservice.

Posted On
May 26, 2010
Posted By
ElPunto

Jacob,

Currently I use a Business Rule to create the SOAP request. Is it possible to call the processor to retrieve the base64encoded ZIp information and add it to the body ?.

My BR code looks like this:

env = new SOAPEnvelope();

env.createNameSpace(“xmlns:ns1″, gns);

var hdr = env.createHeaderElement(‘ns1:AuthenticationInfo’, null, ‘mustUnderstand’, ’0′);

hdr.setAttribute(‘actor’, ‘http://www.w3.org/2001/XMLSchema-instance‘);

hdr.createElement(env, ‘ns1:userName’, gs.getProperty(‘com.snc.interface.tibco.user_name’));

hdr.createElement(env, ‘ns1:password’, gs.getProperty(‘com.snc.interface.tibco.user_password’));

gs.log( ‘Created Header’ );

// Fill static Tibco fields

var bdy = env.createBodyElement(‘ns1:’+ gs.getProperty(‘com.snc.interface.tibco.soap_operation’));

gs.log( ‘Soap Operation: ‘ + gs.getProperty(‘com.snc.interface.tibco.soap_operation’));

env.createElement(bdy, ‘ns1:messagetype’ , gaction);

env.createElement(bdy, ‘ns1:messagecreated’ , cvtDateTime(gs.daysAgo(0)));

env.createElement(bdy, ‘ns1:application’ , gmessagetype);

env.createElement(bdy, ‘ns1:supplier’ , gsupplier);

env.createElement(bdy, ‘ns1:supplier_id’ , gsupplierid);

env.createElement(bdy, ‘ns1:attachment_filename_1′, ‘attachment.zip’);

env.createElement(bdy, ‘ns1:attachment_content_1′ , gattachcontent);

..etc

Can I use the Processor to fill the variable gattachcontent with the base64encoded query ?

What’s the most simple way to base64encode the produced Zip file ?

Best regards

Posted On
May 27, 2010
Posted By
Jacob Andersen

You’ll want to look at this post:

http://www.servicenowguru.com/integration/sending…

Good luck!

Leave a Reply


Notify me of followup comments via e-mail. You can also subscribe without commenting.

Latest Comments

  • Mark Stanger: This linkage all happens for you if you use the task survey plugin. You can look on the wiki for more...
  • Vineeth: I want a way in which if a survey is filled in by the user the response are stored in the survey response...
  • Mark Stanger: This functionality doesn’t connect to an FTP server. See this line in the post above…...
  • Mark Stanger: The report page is back-end XML so there’s no way to directly manipulate the behavior of that...