如何在 IBM Maximo 应用程序套件 (MAS 9) 的 BIRT 报告中显示附件


在我们的 以前的博客,我们介绍了如何在 IBM Maximo 7.6 的 BIRT 报告中直接显示现场技术人员附带的图像。这种方法的工作原理是使用存储在服务器的文件系统中的本地文件路径直接从服务器的文件系统读取图像文件 网址名称 的领域 docinfo 对象。
随着向IBM Maximo应用程序套件(MAS 9)的迁移,该方法不再起作用。 最多 9 是一个云原生平台,附件不再存储在本地文件系统上,它们存储在 云对象存储 (COS),它兼容 S3。这个 网址名称 字段现在包含 COS 引用(例如 cos: doclinks/your-file.jpg)而不是本地路径,因此旧的 File () 方法完全失败了。
本博客详细介绍了在 MAS 9 的 BIRT 报告中从 S3/COS 提取和渲染这些图像的最新方法。
在 Maximo 7.6 中,URLName 值直接指向磁盘上的文件,例如:
/opt/IBM/SMP/maximo/doclinks/Attachments/image.jpg
该脚本仅使用 java.io.File 读取该文件,并将字节传递给 BIRT 动态图像元素,这对于内部部署来说非常简单。
在 最多 9,同一个字段现在包含一个 COS URI,例如:
cos:doclinks/image.jpg
没有本地文件可供读取。该图像存放在 S3 存储桶中,您必须使用 AWS 证书进行身份验证并在报告运行时下载对象。BIRT 报告布局本身(表格单元格中的动态图像)保持完全相同。只需要更改脚本。
该解决方案涉及 BIRT 报告设计器中的两个脚本区域:
报表布局步骤(从中创建数据集) docinfo,将其绑定到表格,插入动态图像元素)与原始博客中描述的相同。如果您尚未完成这些步骤,请参阅中的步骤 1-3 原始指南 第一。
和以前一样,创建一个数据集来获取 网址名称 来自 docinfo 对象,按您的标准(例如特定检查问题或文件类型)过滤。这个 网址名称 列是脚本将用来在 S3 中定位图像的内容。
构造您的表,将其绑定到数据集,然后在相关单元格内放置一个图像元素。将图像源类型设置为 “动态图像” — 这与 Maximo 7.6 相比没有变化。
在报告中 初始化 脚本部分,添加以下内容。它会在报表启动时运行一次,从 Maximo 系统属性中读取 COS 凭证,并创建共享的 S3 客户端:

importPackage(Packages.com.ibm.tivoli.maximo.report.script);
importPackage(Packages.com.amazonaws.auth);
importPackage(Packages.com.amazonaws.services.s3);
importPackage(Packages.com.amazonaws.services.s3.model);
importPackage(Packages.psdi.server);
var accessKey = MXServer.getMXServer().getProperty("mxe.cosaccesskey");
var secretKey = MXServer.getMXServer().getProperty("mxe.cossecretkey");
var bucketName = MXServer.getMXServer().getProperty("mxe.cossysbucket");
// Set up AWS credentials and S3 client
var credentials = new BasicAWSCredentials(accessKey, secretKey);
var s3Client = new AmazonS3Client(credentials);
mxReportScriptContext = MXReportScriptContext.initialize(reportContext);
mxReportScriptContext.setDefaultLogLevel("DEBUG");
mxReportScriptContext.setDefaultLogFile("servicereport.log");
scriptLogger = mxReportScriptContext.getReportScriptLogger();
注意:证书(mxe.cosaccesskey、mxe.cossecretkey、mxe.cossysbucket)直接从 Maximo 的系统属性中读取,因此无需在报告中对敏感值进行硬编码。
在动态图像元素中 onCreate 脚本,添加以下内容。这将触发每行,解析 COS URI,下载图像,处理格式转换,应用 JPEG 压缩并设置显示尺寸:
importPackage(Packages.javax.imageio);
importPackage(Packages.javax.imageio.stream);
importPackage(Packages.java.io);
importPackage(Packages.java.lang);
importPackage(Packages.java.awt.image);
importPackage(Packages.java.awt);
importPackage(Packages.java.net);
importPackage(Packages.com.amazonaws.auth);
importPackage(Packages.com.amazonaws.services.s3);
importPackage(Packages.com.amazonaws.services.s3.model);
if (row["urlName"] != null) {
var fullUrl = String(row["urlName"]);
var prefix = "cos:doclinks/";
var cleanFileName = fullUrl;
var fileName = "";
var fileExtension = "";
if (fullUrl != null) {
if (fullUrl.startsWith(prefix)) {
cleanFileName = fullUrl.substring(prefix.length);
}
var lastSlashIndex = cleanFileName.lastIndexOf("/");
fileName = (lastSlashIndex >= 0)
? cleanFileName.substring(lastSlashIndex + 1) : cleanFileName;
var lastDotIndex = fileName.lastIndexOf(".");
if (lastDotIndex >= 0 && lastDotIndex < fileName.length - 1) {
fileExtension = String(fileName.substring(lastDotIndex + 1).toLowerCase());
}
}
var s3Object = null; var inputStream = null;
var img = null; var bas = null;
var ios = null; var writer = null;
try {
s3Object = s3Client.getObject(new GetObjectRequest(bucketName, cleanFileName));
inputStream = s3Object.getObjectContent();
img = ImageIO.read(inputStream);
if (img == null) {
throw new Error("Failed to read image from S3: " + fileName);
}
// Convert to RGB if needed
if (img.getType() !== BufferedImage.TYPE_INT_RGB) {
var rgbImage = new BufferedImage(
img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
var g = rgbImage.createGraphics();
g.drawImage(img, 0, 0, Color.WHITE, null);
g.dispose();
img = rgbImage;
}
bas = new ByteArrayOutputStream();
var format = (["jpeg","jpg","png","bmp"].indexOf(fileExtension) >= 0)
? fileExtension : "jpeg";
if (format === "jpeg" || format === "jpg") {
var writers = ImageIO.getImageWritersByFormatName("jpeg");
if (!writers.hasNext()) throw new Error("No JPEG writer available");
writer = writers.next();
ios = ImageIO.createImageOutputStream(bas);
writer.setOutput(ios);
var param = writer.getDefaultWriteParam();
if (param.canWriteCompressed()) {
param.setCompressionMode(
javax.imageio.ImageWriteParam.MODE_EXPLICIT);
param.setCompressionQuality(0.3);
}
writer.write(null,
new javax.imageio.IIOImage(img, null, null), param);
} else {
if (!ImageIO.write(img, format, bas)) {
throw new Error("Failed to write image: " + format);
}
}
this.data = bas.toByteArray();
var picWidth = img.getWidth();
if (picWidth > 300) {
var aspectRatio = img.getHeight() / picWidth;
this.width = '300px';
this.height = Math.round(300 * aspectRatio) + 'px';
} else {
this.width = null; this.height = null;
}
} catch (e) {
throw new Error("Error processing '" + fileName + "': " + e.message);
} finally {
if (writer != null) writer.dispose();
if (ios != null) ios.close();
if (bas != null) bas.close();
if (inputStream != null) inputStream.close();
if (s3Object != null) s3Object.close();
}
}
在 BIRT 设计器中预览报告,确认图像呈现正确。验证后,将报告导入 MAS,并根据带有照片附件的工作单或检查单生成 PDF 对其进行测试。

从 Maximo 7.6 迁移到 最多 9 从根本上改变了附件的存储和访问方式。通过从基于本地文件的方法转向基于S3/COS的检索,需要相应地更新支持BIRT报告中动态图像的脚本。
通过初始化脚本处理 S3 身份验证,OnCreate 脚本管理对象下载和图像处理,您可以继续在 MAS 9 中像在 7.6 中一样高效地交付丰富的图像增强报告。
Discover everything you need to know to modernize your asset management strategy.
Inside, you’ll learn:

ActiveG, BPD Zenith, EAM Swiss, InterPro Solutions, Lexco, Peacock Engineering, Projetech, Sharptree, and ZNAPZ have united under one brand: Naviam.
You’ll be redirected to the most relevant page at Naviam.io in a few seconds — or you can
go now.