如何在Node.js应用程序中使用SAP Crystal Reports



我正在尝试在Electron应用程序(桌面Node.js应用程序(中使用SAP Crystal Reports。我有一些*.rpt文件,应该从SQLite数据库读取数据,然后打印一些报告。

但似乎没有一个JavaScript库可以让我做到这一点。我唯一找到的是SAP Crystal Reports JavaScript API,它只是我不使用的SAP Business Intelligence平台的客户端库。我只需要在不依赖任何外部服务器的情况下打印一些报告。

有人能在Node.js应用程序中使用Crystal Reports进行打印吗?

看起来没有办法直接从Node.js应用程序与Crystal Reports交互。因此,我决定编写一个简单的命令行Java程序,该程序使用CrystalReportsJavaSDK。我从Node.js应用程序运行这个Java程序。

Crystal Reports CLI(Java(

我的项目中只有一个cr-cli/CrystalReport.java文件:

import com.crystaldecisions.sdk.occa.report.application.*;
import com.crystaldecisions.sdk.occa.report.data.*;
import com.crystaldecisions.sdk.occa.report.document.*;
import com.crystaldecisions.sdk.occa.report.lib.*;
import com.crystaldecisions.sdk.occa.report.exportoptions.*;
import java.io.*;
import java.nio.file.*;
import org.apache.commons.cli.*;
public class CrystalReport {
public static void main(String args[]) {
Options options = new Options();
Option databaseOption = new Option("d", "database", true, "database file path");
databaseOption.setRequired(true);
options.addOption(databaseOption);
Option exportOption = new Option("e", "export", true, "output PDF file path");
options.addOption(exportOption);
Option reportOption = new Option("r", "report", true, "report file path");
reportOption.setRequired(true);
options.addOption(reportOption);
Option selectionOption = new Option("s", "selection", true, "record selection formula");
selectionOption.setRequired(true);
options.addOption(selectionOption);
CommandLine cmd = null;
try {
CommandLineParser parser = new DefaultParser();
cmd = parser.parse(options, args);
} catch (ParseException e) {
System.out.println(e.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp("cr-cli", options);
System.exit(1);
}
String databasePath = cmd.getOptionValue("database");
String exportPath = cmd.getOptionValue("export");
String reportPath = cmd.getOptionValue("report");
String selectionFormula = cmd.getOptionValue("selection");
try {
CrystalReport report = new CrystalReport(reportPath, databasePath, selectionFormula);
if (exportPath != null) {
Files.createDirectories(Paths.get(exportPath).getParent());
report.export(exportPath);
} else {
report.print();
}
} catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
private ReportClientDocument report;
public CrystalReport(String reportPath, String databasePath, String selectionFormula) throws ReportSDKException {
this.report = new ReportClientDocument();
this.report.open(reportPath, 0);
this.report.setRecordSelectionFormula(selectionFormula);
CrystalReport.replaceDatabaseConnection(this.report, databasePath);
SubreportController subreportController = report.getSubreportController();
for (String subreportName : subreportController.getSubreportNames()) {
ISubreportClientDocument subreport = subreportController.getSubreport(subreportName);
CrystalReport.replaceDatabaseConnection(subreport, databasePath);
}
}
private static void replaceDatabaseConnection(IReportClientDocument report, String databasePath) throws ReportSDKException {
DatabaseController databaseController = report.getDatabaseController();
IConnectionInfo oldConnectionInfo = databaseController.getConnectionInfos(null).getConnectionInfo(0);
PropertyBag attributes = new PropertyBag(oldConnectionInfo.getAttributes());
attributes.put("Connection URL", "jdbc:sqlite:" + databasePath);
IConnectionInfo newConnectionInfo = new ConnectionInfo(oldConnectionInfo);
newConnectionInfo.setAttributes(attributes);
int replaceParams = DBOptions._ignoreCurrentTableQualifiers + DBOptions._doNotVerifyDB;
databaseController.replaceConnection(oldConnectionInfo, newConnectionInfo, null, replaceParams);
}
public void export(String exportPath) throws IOException, FileNotFoundException, ReportSDKException {
ByteArrayInputStream byteArrayInputStream = (ByteArrayInputStream) this.report.getPrintOutputController().export(ReportExportFormat.PDF);
byte byteArray[] = new byte[byteArrayInputStream.available()];
File file = new File(exportPath);
FileOutputStream fileOutputStream = new FileOutputStream(file);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(byteArrayInputStream.available());
int x = byteArrayInputStream.read(byteArray, 0, byteArrayInputStream.available());
byteArrayOutputStream.write(byteArray, 0, x);
byteArrayOutputStream.writeTo(fileOutputStream);
}
public void print() throws ReportSDKException {
this.report.getPrintOutputController().printReport(new PrintReportOptions());
}
}

使用cr-cli/lib文件夹中的这些文件:

com.azalea.ufl.barcode.1.0.jar
commons-configuration-1.2.jar
CrystalCommon2.jar
DatabaseConnectors.jar
JDBInterface.jar
log4j-api.jar
pfjgraphics.jar
sqlite-jdbc-3.41.0.0.jar
XMLConnector.jar
commons-cli-1.5.0.jar
commons-lang-2.1.jar
CrystalReportsRuntime.jar
icu4j.jar
jrcerom.jar
log4j-core.jar
QueryBuilder.jar
webreporting.jar
xpp3.jar
commons-collections-3.2.2.jar
commons-logging.jar
cvom.jar
jai_imageio.jar
keycodeDecoder.jar
logging.jar
sap.com~tc~sec~csi.jar
webreporting-jsf.jar

我使用以下命令编译它:

javac -classpath './cr-cli/lib/*' -source 8 -target 8 ./cr-cli/CrystalReport.java

然后我从我的Electron(Node.js(应用程序中以这种方式运行它:

import {exec} from 'child_process';
import {app} from 'electron';
import path from 'path';
interface CrystalReportsCliOptions {
databasePath: string;
exportPath?: string;
reportPath: string;
selectionFormula: string;
}
const cliPath = path.join(app.isPackaged ? process.resourcesPath : process.cwd(), 'cr-cli');
export async function runCrystalReportsCli(options: CrystalReportsCliOptions) {
const javaVersion = await detectJavaVersion();
if (javaVersion < 8) {
return;
}
const command = ['java'];
if (javaVersion >= 9) {
command.push('--add-exports', 'java.base/sun.security.action=ALL-UNNAMED');
}
command.push('-classpath', `"${cliPath + path.delimiter + path.join(cliPath, 'lib/*')}"`);
command.push('CrystalReport');
command.push('--database', options.databasePath);
command.push('--report', options.reportPath);
command.push('--selection', `"${options.selectionFormula}"`);
if (options.exportPath) {
command.push('--export', options.exportPath);
}
return new Promise((resolve, reject) => {
exec(command.join(' '), (error, stdout) => (error ? reject(error) : resolve(stdout)));
});
}
function detectJavaVersion(): Promise<number> {
return new Promise(resolve => {
exec('java -version', (error, _, stderr) => {
if (error) {
resolve(0);
} else {
const [versionLine] = stderr.split('n');
const version = versionLine.split('"')[1];
const majorVersion = Number(version.split('.')[version.startsWith('1.') ? 1 : 0]);
resolve(majorVersion);
}
});
});
}

最新更新