Thank you for the updated script. I tested it and it work alike a charm !
Original Message:
Sent: Tue January 14, 2025 10:57 AM
From: Jason VenHuizen
Subject: Bulk download the attachments
Ruud,
Here is a version that takes a where clause and then loops over the mbos adding attachments. I have done a really simple 100 record limit, but for a real implementation you might want to take into account things like file size and attachment counts. You could also break this up into pages or zips of zips, but that is beyod the scope of this answer. If you have specific questions feel free to reach out.
-Jason
COSApi = Java.type("com.ibm.tivoli.maximo.cos.COSApi");BufferedOutputStream = Java.type("java.io.BufferedOutputStream");ByteArrayOutputStream = Java.type('java.io.ByteArrayOutputStream');File = Java.type("java.io.File");Files = Java.type("java.nio.file.Files");Paths = Java.type("java.nio.file.Paths");ZipOutputStream = Java.type("java.util.zip.ZipOutputStream");ZipEntry = Java.type("java.util.zip.ZipEntry");MicUtil = Java.type("psdi.iface.mic.MicUtil");MXServer = Java.type('psdi.server.MXServer');System = Java.type("java.lang.System");main();function main() { var lookupSet; try { var mbo; var whereClause; var id; if (typeof request !== undefined) { id = request.getQueryParam("id"); whereClause = request.getQueryParam("where"); var objectName = request.getQueryParam("object"); if(!objectName){ responseBody = JSON.stringify({error: "A Mbo object name (object) query parameter is required."}); return; } lookupSet = MXServer.getMXServer().getMboSet(objectName, userInfo); if(whereClause){ // replace double quotes with single quotes whereClause = whereClause.replaceAll("\"", "'"); lookupSet.setWhere(whereClause); if(lookupSet.isEmpty()){ responseBody = JSON.stringify({error: "The where clause " + whereClause + " returned no records."}); return; }else if (lookupSet.count() >100){ // check that we don't have some massive result set that we can't handle. responseBody = JSON.stringify({error: "The where clause " + whereClause + " returned more than than the maximum result set of 100 records."}); return; } }else if(id){ mbo = lookupSet.getMboForUniqueId(id); }else{ responseBody = JSON.stringify({error: "Either an Mbo Unique Id (id) or where clause (where) query parameter are required."}); return; } } if (typeof mbo !== 'undefined' && mbo) { // if the Mbo has a DOCLINKS relationship we can get all the attachments. if (mbo.getThisMboSet().getMboSetInfo().getRelationInfo("DOCLINKS")) { var docLinksSet = mbo.getMboSet("DOCLINKS"); var paths = []; var docLink = docLinksSet.moveFirst(); while (docLink) { paths.push(docLink.getString("DOCINFO.URLNAME")); docLink = docLinksSet.moveNext(); } if (paths.length > 0) { zipFile = zipFiles(paths); downloadZipFile(zipFile, "attachments.zip",request); } } }else{ var mbo = lookupSet.moveFirst(); var paths = []; while(mbo){ var docLinksSet = mbo.getMboSet("DOCLINKS"); var docLink = docLinksSet.moveFirst(); while (docLink) { paths.push(docLink.getString("DOCINFO.URLNAME")); docLink = docLinksSet.moveNext(); } mbo = lookupSet.moveNext(); } if (paths.length > 0) { zipFile = zipFiles(paths); downloadZipFile(zipFile, "attachments.zip",request); } } } finally { close(lookupSet); }}/** * Fetch and zip the file paths. Handles both local and S3 storage. * @param {Array[String]} files an array of paths to the attached files. * @returns An byte array representing the resulting zip file. */function zipFiles(files) { var bucketName = MicUtil.getProperty("mxe.cosbucketname"); var output = new ByteArrayOutputStream(); var zipOutput = new ZipOutputStream(new BufferedOutputStream(output)); files.forEach(function zipFile(file){ var content; if(file.startsWith("cos")){ var fileName = (new File(file)).getName(); content = new COSApi().getFile(bucketName, fileName);; }else{ content = Files.readAllBytes(Paths.get(file)); } var fileName = (new File(file)).getName(); var zipEntry = new ZipEntry(fileName); zipOutput.putNextEntry(zipEntry); zipOutput.write(content, 0, content.length); zipOutput.flush(); }); zipOutput.flush(); zipOutput.close(); return output.toByteArray(); }/** * Close and clean up the MboSet resources. * @param {MboSet} set the MboSet to close and clean up. */function close(set) { if (set) { set.close(); set.cleanup(); }}/** * Download the zip file to the script request HTTP output. This only works when the script is invoked directly. * @param {byte[]} zipFile The byte array representing the POI workbook, * @param {String} fileName Optional name of the file to export. If not provided "export.xlsx" is used. * @param {com.ibm.tivoli.maximo.oslc.provider.OslcRequest} oslcRequest optional OslcRequest object if an implicit request variable is unavailable. */function downloadZipFile(zipFile, fileName, oslcRequest) { if (typeof request === 'undefined' && !oslcRequest) { throw new Error('The downloadZipFile function can only be called from a direct script invocation with the "request" implicit variable available or with the oslcRequest provided.'); } if (!zipFile) { throw new Error('A zip file is required to export.'); } var response = (typeof request === 'undefined') ? oslcRequest.getHttpServletResponse() : request.getHttpServletResponse(); response.setBufferSize(0); response.setContentType("application/zip"); response.setHeader("content-disposition", 'attachment; filename="' + (fileName ? fileName : 'attachments.zip') + '"'); response.getOutputStream().write(zipFile); response.flushBuffer();}var scriptConfig = { "autoscript": "SHARPTREE.DOWNLOAD.ATTACHMENTS", "description": "Sharptree script to download all attachments.", "version": "1.0.0", "active": true, "logLevel": "ERROR"};
------------------------------
Jason VenHuizen
Sharptree
https://sharptree.io
https://opqo.io
Original Message:
Sent: Mon January 13, 2025 05:54 AM
From: Ruud Peters
Subject: Bulk download the attachments
Hi Jason
Interesting post. Would this also be an option to export all images for a given data set to be used for migration to a new Maximo instance?
i.e. use case: starting from legacy Maximo with attachments instead of item images. Downloading a zip with all item image attachment. So an MBOSet with multiple MBO instead of one.
Can this script be altered to loop through an mbo set and create a zip for each item?
Thanks, Ruud
------------------------------
Ruud Peters
Director Service Software Architecture
Vanderlande
Veghel
Original Message:
Sent: Thu June 08, 2023 05:24 PM
From: Jason VenHuizen
Subject: Bulk download the attachments
Okay, let's do this properly.
Below is a script that you can invoke from a Launch in Context that will download the attachments for the specify object and record id. Assuming you named the script SHARPTREE.DOWNLOAD.ATTACHMENTS (as in the example) and you are this for the Work Order Tracking application in the launch in context URL you will put https://yourmaximoserver/maximo/oslc/script/SHARPTREE.DOWNLOAD.ATTACHMENTS?id={workorderid}&object=WORKORDER
This supports both local files and S3 storage.
Note: You can test this manually by substituting the workorderid (not wonum) and using your browser directly.
The {workorderid} will be replaced by the framework with the current work order id and this will let the script find your record and download the archive.
If you use the VS Code extension you can directly deploy this script, which will save you some time and effort.
https://marketplace.visualstudio.com/items?itemName=sharptree.maximo-script-deploy
This is effectively following the same pattern we used for the Excel export, so you might want to check that out for more details or context for using the Launch in Context.
https://www.sharptree.io/blog/2023/2023-05-22-excel/
If you have any questions feel free to reach out.
Cheers,
Jason
COSApi = Java.type("com.ibm.tivoli.maximo.cos.COSApi");BufferedOutputStream = Java.type("java.io.BufferedOutputStream");ByteArrayOutputStream = Java.type('java.io.ByteArrayOutputStream');File = Java.type("java.io.File");Files = Java.type("java.nio.file.Files");Paths = Java.type("java.nio.file.Paths");ZipOutputStream = Java.type("java.util.zip.ZipOutputStream");ZipEntry = Java.type("java.util.zip.ZipEntry");MicUtil = Java.type("psdi.iface.mic.MicUtil");MXServer = Java.type('psdi.server.MXServer');System = Java.type("java.lang.System");main();function main() { var lookupSet; try { var mbo; if (typeof request !== undefined) { var id = request.getQueryParam("id"); var objectName = request.getQueryParam("object"); lookupSet = MXServer.getMXServer().getMboSet(objectName, userInfo); mbo = lookupSet.getMboForUniqueId(id); } if (typeof mbo !== 'undefined' && mbo) { // if the Mbo has a DOCLINKS relationship we can get all the attachments. if (mbo.getThisMboSet().getMboSetInfo().getRelationInfo("DOCLINKS")) { var docLinksSet = mbo.getMboSet("DOCLINKS"); var paths = []; var docLink = docLinksSet.moveFirst(); while (docLink) { paths.push(docLink.getString("DOCINFO.URLNAME")); docLink = docLinksSet.moveNext(); } if (paths.length > 0) { zipFile = zipFiles(paths); downloadZipFile(zipFile, "attachments.zip",request); } } } } finally { close(lookupSet); }}/** * Fetch and zip the file paths. Handles both local and S3 storage. * @param {Array[String]} files an array of paths to the attached files. * @returns An byte array representing the resulting zip file. */function zipFiles(files) { var bucketName = MicUtil.getProperty("mxe.cosbucketname"); var output = new ByteArrayOutputStream(); var zipOutput = new ZipOutputStream(new BufferedOutputStream(output)); files.forEach(function zipFile(file){ var content; if(file.startsWith("cos")){ var fileName = (new File(file)).getName(); content = new COSApi().getFile(bucketName, fileName);; }else{ content = Files.readAllBytes(Paths.get(file)); } var fileName = (new File(file)).getName(); var zipEntry = new ZipEntry(fileName); zipOutput.putNextEntry(zipEntry); zipOutput.write(content, 0, content.length); zipOutput.flush(); }); zipOutput.flush(); zipOutput.close(); return output.toByteArray(); }/** * Close and clean up the MboSet resources. * @param {MboSet} set the MboSet to close and clean up. */function close(set) { if (set) { set.close(); set.cleanup(); }}/** * Download the zip file to the script request HTTP output. This only works when the script is invoked directly. * @param {byte[]} zipFile The byte array representing the POI workbook, * @param {String} fileName Optional name of the file to export. If not provided "export.xlsx" is used. * @param {com.ibm.tivoli.maximo.oslc.provider.OslcRequest} oslcRequest optional OslcRequest object if an implicit request variable is unavailable. */function downloadZipFile(zipFile, fileName, oslcRequest) { if (typeof request === 'undefined' && !oslcRequest) { throw new Error('The downloadZipFile function can only be called from a direct script invocation with the "request" implicit variable available or with the oslcRequest provided.'); } if (!zipFile) { throw new Error('A zip file is required to export.'); } var response = (typeof request === 'undefined') ? oslcRequest.getHttpServletResponse() : request.getHttpServletResponse(); response.setBufferSize(0); response.setContentType("application/zip"); response.setHeader("content-disposition", 'attachment; filename="' + (fileName ? fileName : 'attachments.zip') + '"'); response.getOutputStream().write(zipFile); response.flushBuffer();}var scriptConfig = { "autoscript": "SHARPTREE.DOWNLOAD.ATTACHMENTS", "description": "Sharptree script to download all attachments.", "version": "1.0.0", "active": true, "logLevel": "ERROR"};
------------------------------
Jason VenHuizen
https://sharptree.io
https://opqo.io
Original Message:
Sent: Thu June 08, 2023 03:20 PM
From: Pablo Condoleo
Subject: Bulk download the attachments
Hi Gaurav I made this script to export PM's attachments to a specific folder on another server.
HashMap = Java.type('java.util.HashMap');
MXServer = Java.type('psdi.server.MXServer');
var dirDest = "<Folder target where you'll put the files>";
var libreria = new HashMap();
libreria.put('service',service);
//var i = 0;
var strPath = null;
var strPathOrig = null;
var archivo = null;
var dirOrig = null;
var userInfo = MXServer.getMXServer().getSystemUserInfo();
PMSet = MXServer.getMXServer().getMboSet("PM",userInfo);
PMSet.setWhere("PM.STATUS = 'ACTIVO'");
PMSet.reset();
pm = PMSet.moveFirst();
while(pm){
doclinkSet = pm.getMboSet("DOCLINKSCIC");
doclink = doclinkSet.moveFirst();
while(doclink){
docinfoSet = doclink.getMboSet("DOCINFO");
docinfo = docinfoSet.getMbo(0);
strPathOrig = docinfo.getString("URLNAME");
strPath = strPathOrig.split("\\");
archivo = strPath[strPath.length-1];
dirOrig = strPathOrig.replace("\\","\\\\");
resultado = service.invokeScript("SCRIPTUTILS").copyFiles(dirOrig, dirDest + archivo);
service.log_info(resultado);
doclink = doclinkSet.moveNext();
}
pm = PMSet.moveNext();
}
Note: SCRIPUTILS is a script that I use as a utils library.
Best Regards
------------------------------
Pablo Condoleo
Original Message:
Sent: Sun May 14, 2023 10:28 PM
From: Gaurav
Subject: Bulk download the attachments
Hi Community,
Please advise if there is a way to download all or multiple attachments, uploaded against a WO, together from Maximo UI.
Currently, we can download one attachment at a time by clicking on the attachment name from View Attachments dialog box, but it becomes bit cumbersome for end users when there are few dozens of attachments against a workorder, and they need to analyse them by downloading it one by one. I am trying to find if there is any way without the customization.
Please note that we are not using Maximo mobile or anywhere solution.
Thanks
------------------------------
Gaurav
------------------------------