目录
一、系统环境以及测试内容
1.系统环境
- IntelliJ IDEA 2019.1.2
- JRE: 1.8.0_202-release-1483-b49 amd64
- Windows 10 64位
- CRYSTAL REPORTS 2008 版本12.0.0.549
- SpringBoot 2.3.4.RELEASE
- Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
2.测试内容
- SpringBoot调用水晶报表文件导出PDF
- 使用JDBC驱动连接oracle数据库
- 参数传值
- 数据筛选
- 图片动态切换
二、设计水晶报表
1.CR2008配置jdbc
(1)在数据库专家中创建新的连接
注意:如果使用Oracle服务器,在SpringBoot项目中会报错:
com.businessobjects.reports.jdbinterface.common.DBException: 查找 JNDI 名称(orcl)时出错
(2)输入JDBC连接信息
jdbc:oracle:thin:@localhost:1521:orcl
oracle.jdbc.driver.OracleDriver
(3)输入用户名密码
(4)解决”未找到JDBC”驱动错误
如果没有配置ojdbc5.jar,会报错
(5)配置JDBC驱动
下载数据库版本对应的ojdbc5.jar
jar包下载地址(使用ojdbc6.jar没有效果)
http://www.oracle.com/technetwork/cn/articles/oem/jdbc-112010-094555-zhs.html
将ojdbc5.jar放在安装目录的...\java\lib目录下
C:\Program Files (x86)\Business Objects\Common\4.0\java\lib
修改配置文件CRConfig.xml,在<Classpath>中添加ojdbc5.jar文件路径,重启CR2008即可连接到数据库
C:\Program Files (x86)\Business Objects\Common\4.0\java\CRConfig.xml
修改配置文件
2.报表设计
(1)新建test.rpt报表
数据展示,连接jdbc,选择DEPT表,将字段拖动到详细资料;
插入图片并设置图形位置
设计选择公式筛选数据。
(2)添加参数字段
添加2个参数
deptNoPar:数据筛选
imagePath:动态更改图片
(3)设置图片地址
使用参数imagePath
(4)选择公式
数据筛选使用参数deptNoPar
(5)报表预览
三、SpringBoot项目搭建
1.新建SpringBoot项目
(1)pom.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.asyf</groupId>
<artifactId>crytstal-report-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!--设置为 true 则跳过测试-->
<skip>true</skip>
<compilerArgs>
<arg>-extdirs</arg>
<arg>${project.basedir}/lib</arg>
</compilerArgs>
</configuration>
</plugin>
</plugins>
<resources>
<!--拷贝jar-->
<resource>
<!--需要打包的jar路径-->
<directory>lib</directory>
<!--复制到的路径-->
<targetPath>BOOT-INF/lib/</targetPath>
<includes>
<include>**/*.jar</include>
</includes>
</resource>
<!--不配置这个会丢失resources目录的文件-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*</include>
</includes>
<!--To prevent corrupting your binary files when filtering is enabled, you can configure a list of file extensions that will not be filtered.
翻译:为了避免开启 filter后你的二进制文件被损毁,你可配置不被过滤的文件拓展名列表-->
<!--必须设置成fasle否则rpt文件签名不一致,造成水晶报表无法正确显示-->
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
(2)添加水晶报表文件
将第二步设计好的水晶报表文件拷贝到项目resources目录
2.SpringBoot添加水晶报表jar包
(1)下载jar包
链接地址
https://origin.softwaredownloads.sap.com/public/site/index.html
(2)SpringBoot项目引入jar包
将压缩包下面的lib拷贝到SpringBoot项目
(3)拷贝ojdbc5.jar到lib
SpringBoot项目添加本地jar
3.水晶报表开源项目-CRJavaHelper工具类
在这里面下载CRJavaHelper工具类使用,没有找到官网java文档
https://github.com/souvikduttachoudhury/CrystalReportsSpringBoot.git
src/com/businessobjects/samples/CRJavaHelper.java
使用CRJavaHelper工具类操作水晶报表,将其拷贝到项目中
CRJavaHelper .java
/**
* This sample code is an example of how to use the Business Objects APIs.
* Because the sample code is designed for demonstration only, it is
* unsupported. You are free to modify and distribute the sample code as needed.
*/
package com.asyf.demo.utils;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Collection;
import java.util.Locale;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import com.crystaldecisions.sdk.occa.report.application.DataDefController;
import com.crystaldecisions.sdk.occa.report.application.ReportClientDocument;
import com.crystaldecisions.sdk.occa.report.data.FieldDisplayNameType;
import com.crystaldecisions.sdk.occa.report.data.IConnectionInfo;
import com.crystaldecisions.sdk.occa.report.data.ITable;
import com.crystaldecisions.sdk.occa.report.data.ParameterField;
import com.crystaldecisions.sdk.occa.report.data.ParameterFieldDiscreteValue;
import com.crystaldecisions.sdk.occa.report.data.ParameterFieldRangeValue;
import com.crystaldecisions.sdk.occa.report.data.RangeValueBoundType;
import com.crystaldecisions.sdk.occa.report.data.Tables;
import com.crystaldecisions.sdk.occa.report.data.Values;
import com.crystaldecisions.sdk.occa.report.document.PaperSize;
import com.crystaldecisions.sdk.occa.report.document.PaperSource;
import com.crystaldecisions.sdk.occa.report.document.PrintReportOptions;
import com.crystaldecisions.sdk.occa.report.document.PrinterDuplex;
import com.crystaldecisions.sdk.occa.report.exportoptions.CharacterSeparatedValuesExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.DataOnlyExcelExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.EditableRTFExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.ExportOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.PDFExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.RTFWordExportFormatOptions;
import com.crystaldecisions.sdk.occa.report.exportoptions.ReportExportFormat;
import com.crystaldecisions.sdk.occa.report.lib.IStrings;
import com.crystaldecisions.sdk.occa.report.lib.PropertyBag;
import com.crystaldecisions.sdk.occa.report.lib.ReportSDKException;
import com.crystaldecisions.sdk.occa.report.lib.ReportSDKExceptionBase;
/**
* Crystal Reports Java Helper Sample
* ************************
* Please note that you need to define a runtime server in order for this class
* to compile.
* ************************
* @author Business Objects
*/
public class CRJavaHelper {
/**
* Logs on to all existing datasource
*
* @param clientDoc The reportClientDocument representing the report being used
* @param username The DB logon user name
* @param password The DB logon password
* @throws ReportSDKException
*/
public static void logonDataSource(ReportClientDocument clientDoc,
String username, String password) throws ReportSDKException {
clientDoc.getDatabaseController().logon(username, password);
}
/**
* Changes the DataSource for each Table
* @param clientDoc The reportClientDocument representing the report being used
* @param username The DB logon user name
* @param password The DB logon password
* @param connectionURL The connection URL
* @param driverName The driver Name
* @param jndiName The JNDI name
* @throws ReportSDKException
*/
public static void changeDataSource(ReportClientDocument clientDoc,
String username, String password, String connectionURL,
String driverName,String jndiName) throws ReportSDKException {
changeDataSource(clientDoc, null, null, username, password, connectionURL, driverName, jndiName);
}
/**
* Changes the DataSource for a specific Table
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName "" for main report, name of subreport for subreport, null for all reports
* @param tableName name of table to change. null for all tables.
* @param username The DB logon user name
* @param password The DB logon password
* @param connectionURL The connection URL
* @param driverName The driver Name
* @param jndiName The JNDI name
* @throws ReportSDKException
*/
public static void changeDataSource(ReportClientDocument clientDoc,
String reportName, String tableName,
String username, String password, String connectionURL,
String driverName,String jndiName) throws ReportSDKException {
PropertyBag propertyBag = null;
IConnectionInfo connectionInfo = null;
ITable origTable = null;
ITable newTable = null;
// Declare variables to hold ConnectionInfo values.
// Below is the list of values required to switch to use a JDBC/JNDI
// connection
String TRUSTED_CONNECTION = "false";
String SERVER_TYPE = "JDBC (JNDI)";
String USE_JDBC = "true";
String DATABASE_DLL = "crdb_jdbc.dll";
String JNDI_DATASOURCE_NAME = jndiName;
String CONNECTION_URL = connectionURL;
String DATABASE_CLASS_NAME = driverName;
// The next few parameters are optional parameters which you may want to
// uncomment
// You may wish to adjust the arguments of the method to pass these
// values in if necessary
// String TABLE_NAME_QUALIFIER = "new_table_name";
// String SERVER_NAME = "new_server_name";
// String CONNECTION_STRING = "new_connection_string";
// String DATABASE_NAME = "new_database_name";
// String URI = "new_URI";
// Declare variables to hold database User Name and Password values
String DB_USER_NAME = username;
String DB_PASSWORD = password;
// Obtain collection of tables from this database controller
if (reportName == null || reportName.equals("")) {
Tables tables = clientDoc.getDatabaseController().getDatabase().getTables();
for(int i = 0;i < tables.size();i++){
origTable = tables.getTable(i);
if (tableName == null || origTable.getName().equals(tableName)) {
newTable = (ITable)origTable.clone(true);
// We set the Fully qualified name to the Table Alias to keep the
// method generic
// This workflow may not work in all scenarios and should likely be
// customized to work
// in the developer's specific situation. The end result of this
// statement will be to strip
// the existing table of it's db specific identifiers. For example
// Xtreme.dbo.Customer becomes just Customer
newTable.setQualifiedName(origTable.getAlias());
// Change properties that are different from the original datasource
// For example, if the table name has changed you will be required
// to change it during this routine
// table.setQualifiedName(TABLE_NAME_QUALIFIER);
// Change connection information properties
connectionInfo = newTable.getConnectionInfo();
// Set new table connection property attributes
propertyBag = new PropertyBag();
// Overwrite any existing properties with updated values
propertyBag.put("Trusted_Connection", TRUSTED_CONNECTION);
propertyBag.put("Server Type", SERVER_TYPE);
propertyBag.put("Use JDBC", USE_JDBC);
propertyBag.put("Database DLL",DATABASE_DLL );
propertyBag.put("JNDI Datasource Name",JNDI_DATASOURCE_NAME );
propertyBag.put("Connection URL", CONNECTION_URL);
propertyBag.put("Database Class Name", DATABASE_CLASS_NAME);
// propertyBag.put("Server Name", SERVER_NAME); //Optional property
// propertyBag.put("Connection String", CONNECTION_STRING); //Optional property
// propertyBag.put("Database Name", DATABASE_NAME); //Optional property
// propertyBag.put("URI", URI); //Optional property
connectionInfo.setAttributes(propertyBag);
// Set database username and password
// NOTE: Even if the username and password properties do not change
// when switching databases, the
// database password is *not* saved in the report and must be set at
// runtime if the database is secured.
connectionInfo.setUserName(DB_USER_NAME);
connectionInfo.setPassword(DB_PASSWORD);
// Update the table information
clientDoc.getDatabaseController().setTableLocation(origTable, newTable);
}
}
}
// Next loop through all the subreports and pass in the same
// information. You may consider
// creating a separate method which accepts
if (reportName == null || !(reportName.equals(""))) {
IStrings subNames = clientDoc.getSubreportController().getSubreportNames();
for (int subNum=0;subNum<subNames.size();subNum++) {
Tables tables = clientDoc.getSubreportController().getSubreport(subNames.getString(subNum)).getDatabaseController().getDatabase().getTables();
for(int i = 0;i < tables.size();i++){
origTable = tables.getTable(i);
if (tableName == null || origTable.getName().equals(tableName)) {
newTable = (ITable)origTable.clone(true);
// We set the Fully qualified name to the Table Alias to keep
// the method generic
// This workflow may not work in all scenarios and should likely
// be customized to work
// in the developer's specific situation. The end result of this
// statement will be to strip
// the existing table of it's db specific identifiers. For
// example Xtreme.dbo.Customer becomes just Customer
newTable.setQualifiedName(origTable.getAlias());
// Change properties that are different from the original
// datasource
// table.setQualifiedName(TABLE_NAME_QUALIFIER);
// Change connection information properties
connectionInfo = newTable.getConnectionInfo();
// Set new table connection property attributes
propertyBag = new PropertyBag();
// Overwrite any existing properties with updated values
propertyBag.put("Trusted_Connection", TRUSTED_CONNECTION);
propertyBag.put("Server Type", SERVER_TYPE);
propertyBag.put("Use JDBC", USE_JDBC);
propertyBag.put("Database DLL",DATABASE_DLL );
propertyBag.put("JNDI Datasource Name",JNDI_DATASOURCE_NAME );
propertyBag.put("Connection URL", CONNECTION_URL);
propertyBag.put("Database Class Name", DATABASE_CLASS_NAME);
// propertyBag.put("Server Name", SERVER_NAME); //Optional property
// propertyBag.put("Connection String", CONNECTION_STRING); //Optional property
// propertyBag.put("Database Name", DATABASE_NAME); //Optional property
// propertyBag.put("URI", URI); //Optional property
connectionInfo.setAttributes(propertyBag);
// Set database username and password
// NOTE: Even if the username and password properties do not
// change when switching databases, the
// database password is *not* saved in the report and must be
// set at runtime if the database is secured.
connectionInfo.setUserName(DB_USER_NAME);
connectionInfo.setPassword(DB_PASSWORD);
// Update the table information
clientDoc.getSubreportController().getSubreport(subNames.getString(subNum)).getDatabaseController().setTableLocation(origTable, newTable);
}
}
}
}
}
/**
* Passes a populated java.sql.Resultset object to a Table object
*
* @param clientDoc The reportClientDocument representing the report being used
* @param rs The java.sql.Resultset used to populate the Table
* @param tableAlias The alias of the table
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @throws ReportSDKException
*/
public static void passResultSet(ReportClientDocument clientDoc, java.sql.ResultSet rs,
String tableAlias, String reportName) throws ReportSDKException {
if(reportName.equals(""))
clientDoc.getDatabaseController().setDataSource(rs, tableAlias,tableAlias);
else
clientDoc.getSubreportController().getSubreport(reportName).getDatabaseController().setDataSource(rs, tableAlias,tableAlias);
}
/**
* Passes a populated collection of a Java class to a Table object
*
* @param clientDoc The reportClientDocument representing the report being used
* @param dataSet The java.sql.Resultset used to populate the Table
* @param className The fully-qualified class name of the POJO objects being passed
* @param tableAlias The alias of the table
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @throws ReportSDKException
*/
public static void passPOJO(ReportClientDocument clientDoc, @SuppressWarnings("rawtypes") Collection dataSet,
String className, String tableAlias, String reportName) throws ReportSDKException,ClassNotFoundException{
if(reportName.equals(""))
clientDoc.getDatabaseController().setDataSource(dataSet, Class.forName(className),tableAlias,tableAlias);
else
clientDoc.getSubreportController().getSubreport(reportName).getDatabaseController().setDataSource(dataSet, Class.forName(className),tableAlias,tableAlias);
}
/**
* Passes a single discrete parameter value to a report parameter
*
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @param parameterName The name of the parameter
* @param newValue The new value of the parameter
* @throws ReportSDKException
*/
public static void addDiscreteParameterValue(ReportClientDocument clientDoc, String reportName, String parameterName, Object newValue) throws ReportSDKException{
DataDefController dataDefController = null;
if(reportName.equals(""))
dataDefController = clientDoc.getDataDefController();
else
dataDefController = clientDoc.getSubreportController().getSubreport(reportName).getDataDefController();
ParameterFieldDiscreteValue newDiscValue = new ParameterFieldDiscreteValue();
newDiscValue.setValue(newValue);
ParameterField paramField = (ParameterField)dataDefController.getDataDefinition().getParameterFields().findField(parameterName, FieldDisplayNameType.fieldName, Locale.getDefault());
System.out.println(paramField.getName());
boolean multiValue = paramField.getAllowMultiValue();
if(multiValue) {
Values newVals = (Values)paramField.getCurrentValues().clone(true);
newVals.add(newDiscValue);
clientDoc.getDataDefController().getParameterFieldController().setCurrentValue(reportName, parameterName ,newVals);
} else {
clientDoc.getDataDefController().getParameterFieldController().setCurrentValue(reportName, parameterName , newValue);
}
}
/**
* Passes multiple discrete parameter values to a report parameter
*
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @param parameterName The name of the parameter
* @param newValues An array of new values to get set on the parameter
* @throws ReportSDKException
*/
public static void addDiscreteParameterValue(ReportClientDocument clientDoc, String reportName, String parameterName, Object[] newValues) throws ReportSDKException{
clientDoc.getDataDefController().getParameterFieldController().setCurrentValues(reportName, parameterName ,newValues);
}
/**
* Passes a single range parameter value to a report parameter. The range is assumed to
* be inclusive on beginning and end.
*
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @param parameterName The name of the parameter
* @param beginValue The value of the beginning of the range
* @param endValue The value of the end of the range
* @throws ReportSDKException
*/
public static void addRangeParameterValue(ReportClientDocument clientDoc, String reportName, String parameterName, Object beginValue, Object endValue) throws ReportSDKException{
addRangeParameterValue(clientDoc, reportName, parameterName, beginValue, RangeValueBoundType.inclusive, endValue, RangeValueBoundType.inclusive);
}
/**
* Passes multiple range parameter values to a report parameter.
*
* This overload of the addRangeParameterValue will only work if the
* parameter is setup to accept multiple values.
*
* If the Parameter does not accept multiple values then it is expected that
* this version of the method will return an error
*
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @param parameterName The name of the parameter
* @param beginValues Array of beginning values. Must be same length as endValues.
* @param endValues Array of ending values. Must be same length as beginValues.
* @throws ReportSDKException
*/
public static void addRangeParameterValue(ReportClientDocument clientDoc, String reportName, String parameterName, Object[] beginValues, Object[] endValues) throws ReportSDKException{
addRangeParameterValue(clientDoc, reportName, parameterName, beginValues, RangeValueBoundType.inclusive, endValues, RangeValueBoundType.inclusive);
}
/**
* Passes a single range parameter value to a report parameter
*
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @param parameterName The name of the parameter
* @param beginValue The value of the beginning of the range
* @param lowerBoundType The inclusion/exclusion range of the start of range.
* @param endValue The value of the end of the range
* @param upperBoundType The inclusion/exclusion range of the end of range.
* @throws ReportSDKException
*/
public static void addRangeParameterValue(ReportClientDocument clientDoc, String reportName, String parameterName, Object beginValue, RangeValueBoundType lowerBoundType,Object endValue, RangeValueBoundType upperBoundType) throws ReportSDKException{
DataDefController dataDefController = null;
if(reportName.equals(""))
dataDefController = clientDoc.getDataDefController();
else
dataDefController = clientDoc.getSubreportController().getSubreport(reportName).getDataDefController();
ParameterFieldRangeValue newRangeValue = new ParameterFieldRangeValue();
newRangeValue.setBeginValue(beginValue);
newRangeValue.setLowerBoundType(lowerBoundType);
newRangeValue.setEndValue(endValue);
newRangeValue.setUpperBoundType(upperBoundType);
ParameterField paramField = (ParameterField)dataDefController.getDataDefinition().getParameterFields().findField(parameterName, FieldDisplayNameType.fieldName, Locale.getDefault());
boolean multiValue = paramField.getAllowMultiValue();
if (multiValue) {
Values newVals = (Values)paramField.getCurrentValues().clone(true);
newVals.add(newRangeValue);
clientDoc.getDataDefController().getParameterFieldController().setCurrentValue(reportName, parameterName , newVals);
} else {
clientDoc.getDataDefController().getParameterFieldController().setCurrentValue(reportName, parameterName , newRangeValue);
}
}
/**
* Passes multiple range parameter values to a report parameter.
*
* This overload of the addRangeParameterValue will only work if the
* parameter is setup to accept multiple values.
*
* If the Parameter does not accept multiple values then it is expected that
* this version of the method will return an error
*
* @param clientDoc The reportClientDocument representing the report being used
* @param reportName The name of the subreport. If tables in the main report
* is to be used, "" should be passed
* @param parameterName The name of the parameter
* @param beginValues Array of beginning values. Must be same length as endValues.
* @param lowerBoundType The inclusion/exclusion range of the start of range.
* @param endValues Array of ending values. Must be same length as beginValues.
* @param upperBoundType The inclusion/exclusion range of the end of range.
*
* @throws ReportSDKException
*/
public static void addRangeParameterValue(ReportClientDocument clientDoc, String reportName, String parameterName, Object[] beginValues,RangeValueBoundType lowerBoundType, Object[] endValues, RangeValueBoundType upperBoundType) throws ReportSDKException{
// it is expected that the beginValues array is the same size as the
// endValues array
ParameterFieldRangeValue[] newRangeValues = new ParameterFieldRangeValue[beginValues.length];
for(int i=0;i<beginValues.length;i++){
newRangeValues[i] = new ParameterFieldRangeValue();
newRangeValues[i].setBeginValue(beginValues[i]);
newRangeValues[i].setLowerBoundType(lowerBoundType);
newRangeValues[i].setEndValue(endValues[i]);
newRangeValues[i].setUpperBoundType(upperBoundType);
}
clientDoc.getDataDefController().getParameterFieldController().setCurrentValues(reportName, parameterName , newRangeValues);
}
/**
* Exports a report to PDF
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param startPage Starting page
* @param endPage Ending page
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportPDF(ReportClientDocument clientDoc, HttpServletResponse response, boolean attachment) throws ReportSDKExceptionBase, IOException {
// PDF export allows page range export. The following routine ensures
// that the requested page range is valid
PDFExportFormatOptions pdfOptions = new PDFExportFormatOptions();
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.PDF);
exportOptions.setFormatOptions(pdfOptions);
export(clientDoc, exportOptions, response, attachment, "application/pdf", "pdf");
}
/**
* Exports a report to PDF for a range of pages
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param startPage Starting page
* @param endPage Ending page
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportPDF(ReportClientDocument clientDoc, HttpServletResponse response, ServletContext context, int startPage, int endPage,boolean attachment) throws ReportSDKExceptionBase, IOException {
// PDF export allows page range export. The following routine ensures
// that the requested page range is valid
PDFExportFormatOptions pdfOptions = new PDFExportFormatOptions();
pdfOptions.setStartPageNumber(startPage);
pdfOptions.setEndPageNumber(endPage);
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.PDF);
exportOptions.setFormatOptions(pdfOptions);
export(clientDoc, exportOptions, response, attachment, "application/pdf", "pdf");
}
/**
* Exports a report to RTF
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportRTF(ReportClientDocument clientDoc, HttpServletResponse response, boolean attachment) throws ReportSDKExceptionBase, IOException {
// RTF export allows page range export. The following routine ensures
// that the requested page range is valid
RTFWordExportFormatOptions rtfOptions = new RTFWordExportFormatOptions();
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.RTF);
exportOptions.setFormatOptions(rtfOptions);
export(clientDoc, exportOptions, response, attachment, "text/rtf", "rtf");
}
/**
* Exports a report to RTF for a range of pages
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param startPage Starting page
* @param endPage Ending page.
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportRTF(ReportClientDocument clientDoc, HttpServletResponse response, ServletContext context, int startPage, int endPage, boolean attachment) throws ReportSDKExceptionBase, IOException {
// RTF export allows page range export. The following routine ensures
// that the requested page range is valid
RTFWordExportFormatOptions rtfOptions = new RTFWordExportFormatOptions();
rtfOptions.setStartPageNumber(startPage);
rtfOptions.setEndPageNumber(endPage);
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.RTF);
exportOptions.setFormatOptions(rtfOptions);
export(clientDoc, exportOptions, response, attachment, "text/rtf", "rtf");
}
/**
* Exports a report to RTF
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportRTFEditable(ReportClientDocument clientDoc, HttpServletResponse response, boolean attachment) throws ReportSDKExceptionBase, IOException {
// RTF export allows page range export. The following routine ensures
// that the requested page range is valid
EditableRTFExportFormatOptions rtfOptions = new EditableRTFExportFormatOptions();
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.editableRTF);
exportOptions.setFormatOptions(rtfOptions);
export(clientDoc, exportOptions, response, attachment, "text/rtf", "rtf");
}
/**
* Exports a report to RTF for a range of pages
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param startPage Starting page
* @param endPage Ending page.
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportRTFEditable(ReportClientDocument clientDoc, HttpServletResponse response, ServletContext context, int startPage, int endPage,boolean attachment) throws ReportSDKExceptionBase, IOException {
// RTF export allows page range export. The following routine ensures
// that the requested page range is valid
EditableRTFExportFormatOptions rtfOptions = new EditableRTFExportFormatOptions();
rtfOptions.setStartPageNumber(startPage);
rtfOptions.setEndPageNumber(endPage);
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.editableRTF);
exportOptions.setFormatOptions(rtfOptions);
export(clientDoc, exportOptions, response, attachment, "text/rtf", "rtf");
}
/**
* Exports a report to Excel (Data Only)
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportExcelDataOnly(ReportClientDocument clientDoc, HttpServletResponse response, boolean attachment) throws ReportSDKExceptionBase, IOException {
DataOnlyExcelExportFormatOptions excelOptions = new DataOnlyExcelExportFormatOptions();
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.recordToMSExcel);
exportOptions.setFormatOptions(excelOptions );
export(clientDoc, exportOptions, response, attachment, "application/excel", "xls");
}
/**
* Exports a report to CSV
*
* @param clientDoc The reportClientDocument representing the report being used
* @param response The HttpServletResponse object
* @param attachment true to prompts for open or save; false opens the report
* in the specified format after exporting.
* @throws ReportSDKExceptionBase
* @throws IOException
*/
public static void exportCSV(ReportClientDocument clientDoc, HttpServletResponse response, boolean attachment) throws ReportSDKExceptionBase, IOException {
CharacterSeparatedValuesExportFormatOptions csvOptions = new CharacterSeparatedValuesExportFormatOptions();
csvOptions.setSeparator(",");
csvOptions.setDelimiter("\n");
ExportOptions exportOptions = new ExportOptions();
exportOptions.setExportFormatType(ReportExportFormat.characterSeparatedValues);
exportOptions.setFormatOptions(csvOptions);
export(clientDoc, exportOptions, response, attachment, "text/csv", "csv");
}
/**
* Exports a report to a specified format
*
* @param clientDoc The reportClientDocument representing the report being used
* @param exportOptions Export options
* @param response The response object to write to
* @param attachment True to prompts for open or save; false opens the report
* in the specified format after exporting.
* @param mimeType MIME type of the format being exported
* @param extension file extension of the format (e.g., "pdf" for Acrobat)
* @throws ReportSDKExceptionBase
* @throws IOException
*/
private static void export(ReportClientDocument clientDoc, ExportOptions exportOptions, HttpServletResponse response, boolean attachment, String mimeType, String extension)
throws ReportSDKExceptionBase, IOException {
InputStream is = null;
try {
is = new BufferedInputStream(clientDoc.getPrintOutputController().export(exportOptions));
byte[] data = new byte[1024];
response.setContentType(mimeType);
if (attachment)
{
String name = clientDoc.getReportSource().getReportTitle();
if (name == null)
{
name = "CrystalReportViewer";
}
else
{
name = name.replaceAll("\"", "");
}
response.setHeader("Content-Disposition",
"attachment; filename=\"" + name + "."+extension+"\"");
}
OutputStream os = response.getOutputStream();
while (is.read(data) > -1) {
os.write(data);
}
} finally {
if (is != null) {
is.close();
}
}
}
/**
* Prints to the server printer
*
* @param clientDoc The reportClientDocument representing the report being used
* @param printerName Name of printer used to print the report
* @throws ReportSDKException
*/
public static void printToServer(ReportClientDocument clientDoc,String printerName)throws ReportSDKException {
PrintReportOptions printOptions = new PrintReportOptions();
// Note: Printer with the <printer name> below must already be
// configured.
printOptions.setPrinterName(printerName);
printOptions.setJobTitle("Sample Print Job from Crystal Reports.");
printOptions.setPrinterDuplex(PrinterDuplex.useDefault);
printOptions.setPaperSource(PaperSource.auto);
printOptions.setPaperSize(PaperSize.paperLetter);
printOptions.setNumberOfCopies(1);
printOptions.setCollated(false);
// Print report
clientDoc.getPrintOutputController().printReport(printOptions);
}
/**
* Prints a range of pages to the server printer
*
* @param clientDoc The reportClientDocument representing the report being used
* @param printerName Name of printer used to print the report
* @param startPage Starting page
* @param endPage Ending page.
* @throws ReportSDKException
*/
public static void printToServer(ReportClientDocument clientDoc,String printerName,int startPage, int endPage)throws ReportSDKException {
PrintReportOptions printOptions = new PrintReportOptions();
// Note: Printer with the <printer name> below must already be
// configured.
printOptions.setPrinterName(printerName);
printOptions.setJobTitle("Sample Print Job from Crystal Reports.");
printOptions.setPrinterDuplex(PrinterDuplex.useDefault);
printOptions.setPaperSource(PaperSource.auto);
printOptions.setPaperSize(PaperSize.paperLetter);
printOptions.setNumberOfCopies(1);
printOptions.setCollated(false);
PrintReportOptions.PageRange printPageRange = new PrintReportOptions.PageRange(startPage,endPage);
printOptions.addPrinterPageRange(printPageRange);
// Print report
clientDoc.getPrintOutputController().printReport(printOptions);
}
}
4.编写测试接口
package com.asyf.demo.controller;
import com.asyf.demo.utils.CRJavaHelper;
import com.crystaldecisions.sdk.occa.report.application.DataDefController;
import com.crystaldecisions.sdk.occa.report.application.ReportClientDocument;
import com.crystaldecisions.sdk.occa.report.data.Fields;
import com.crystaldecisions.sdk.occa.report.data.IParameterField;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
/**
* @Author admin
* <p>
* 开源项目地址,CRJavaHelper的来源
* https://github.com/souvikduttachoudhury/CrystalReportsSpringBoot
*/
@RestController
@RequestMapping(value = "/")
public class CrystalReportDemo {
//请求地址需要转码,http://localhost:8080/test?deptNoPar=10&imagePath=C%3A%5CUsers%5CAdministrator%5CDesktop%5Cc.png
@RequestMapping(value = "test", method = RequestMethod.GET)
public void test2(HttpServletResponse response, Integer deptNoPar, String imagePath) throws Exception {
//加载报表文件
ReportClientDocument r = new ReportClientDocument();
r.open("static/test.rpt", 0);
//更换数据源
String username = "scott";
String password = "root";
String driverName = "oracle.jdbc.OracleDriver";
String connectionURL = "jdbc:oracle:thin:@localhost:1521:orcl";
String jndiName = "JDBC(JNDI)";
CRJavaHelper.changeDataSource(r, username, password, connectionURL, driverName, jndiName);
//查询参数
DataDefController dataDefController = r.getDataDefController();
Fields<IParameterField> fields = dataDefController.getDataDefinition().getParameterFields();
System.out.println("参数数量:" + fields.size());
for (IParameterField field : fields) {
System.out.println("参数 - " + field.getName());
}
//给参数赋值
//第二个参数代表子报表,设置主报表参数,设置为空串
CRJavaHelper.addDiscreteParameterValue(r, "", "deptNoPar", deptNoPar);
CRJavaHelper.addDiscreteParameterValue(r, "", "imagePath", imagePath);
//导出PDF
CRJavaHelper.exportPDF(r, response, false);
}
}
四、项目测试
1.注意事项
(1)请求地址中的imagePath内容需要转码
可以在线转码,推荐网址:http://www.jsons.cn/urlencode/
(2)输入不存在的imagePath地址,会使用水晶报表的默认图片
2.浏览器测试
3.动态效果