Springboot 整合 ireporter 实践

时间:2022-12-18 07:54:23

背景

这段时间,在做项目时,设计到需要带参数的批量生成报告。尝试了很多方法,包括grafana等BI工具。虽然grafana这种BI工具可以在线查看,但是无法导出。甚至当想把报告整合邮件发送时,grafana就显得无能为力了。于是在搜索了各个工具后发现了 ireporter 。不得不说,现在这个时代,我们遇到的无法解决研发场景,肯定有人也遇到了,并且已经有了很好的解决方案。

ireporter支持实现编辑好报告模板,然后通过sql查询和参数控制,实现报告的在线生成。并提供导出pdf和html的格式,支持报告生成后直接访问和下载。或作为邮件的附件,直接发送,解决了我的场景。

废话不多说,直接上代码。

事前准备

在整合之前,你需要提前准备好 ireporter 的模板文件。ireporter的下载安装,网上有很多教程,此处就不再赘述,可自行搜索相关内容。

模板准备步骤:

  1. 安装好软件之后,打开软件,配置一个简单的报告模板,并保存。

  2. 右键项目名称,选择【compile report】编译报告。
    Springboot 整合 ireporter 实践

  3. 将【*.jasper】模板文件放到项目工程下
    Springboot 整合 ireporter 实践

整合 spring boot 工程

maven引用

<!--https://mvnrepository.com/artifact/com.itextpdf/itext-asian-->
<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports</artifactId>
    <version>6.17.0</version>
</dependency>
<dependency>
    <groupId>net.sf.jasperreports</groupId>
    <artifactId>jasperreports-fonts</artifactId>
    <version>6.17.0</version>
</dependency>
<dependency>
    <groupId>com.lowagie</groupId>
    <artifactId>iTextAsian</artifactId>
    <version>5.2.0.2</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/iTextAsian_5.2.0.2.jar</systemPath>
</dependency>
<dependency>
    <groupId>net.sf.barcode4j</groupId>
    <artifactId>barcode4j</artifactId>
    <version>2.1</version>
</dependency>
<dependency>
    <groupId>net.sourceforge.barbecue</groupId>
    <artifactId>barbecue</artifactId>
    <version>1.5-beta1</version>
</dependency>
<dependency>
    <groupId>org.apache.xmlgraphics</groupId>
    <artifactId>batik-bridge</artifactId>
    <version>1.14</version>
</dependency>

数据连接

ireporter在生成报表时,需要读取数据库数据,因此需要一个数据库连接。此处为了说明,简单创建了一个数据库连接。实际项目中,需要对该工具类做适当的封装。

public class ConnectionProvider {  
  
    // 数据库连接信息,这里由于我的模板使用的是运营中心数据库,这里默认用研发云查询库数据库作为数据源
    // 实际项目中,需要对此部分配置信息进行封装
    private static String driverClassName ="com.mysql.jdbc.Driver";  
    private static String username="username";  
    private static String password="password";  
    private static String url="jdbc:mysql://ip:port/databaseName?useSSL=false";  
  
    static{  
        try {  
            Class.forName(driverClassName);  
        } catch (ClassNotFoundException e) {  
            throw new RuntimeException(e);  
        }  
    }  
    public static Connection getConnection(){  
        try {  
            return DriverManager.getConnection(url, username, password);  
        } catch (SQLException e) {  
            throw new RuntimeException(e);  
        }  
    }  
}

编写工具类

编写工具类,调用ireporter报告模板,生成相应的报告。

@Component  
public class JasperReporterHandle {  
  
    private static final Logger LOGGER = LoggerFactory.getLogger(JasperReporterHandle.class);  
  
    /**  
     * 导出报表为 pdf  
     * @param templateName ireporter工具生成的模板名称,使用全路径  
     * @param params 报告模板中定义的参数列表  
     * @param filePath 导出的pdf文件名称,使用全路径。实际开发中,此参数可省略,直接返回文件流,共页面直接下载。  
     */  
    public void processReporter2PDF(String templateName, Map<String, Object> params, String filePath) {  
  
        //1.读取模板文件  
        Resource resource = new ClassPathResource(templateName);  
  
        FileInputStream fis = null;  
        FileOutputStream fileOutputStream = null;  
  
        // 2. 获取数据库连接  
        Connection connection = ConnectionProvider.getConnection();  
  
        try {  
  
            fis = new FileInputStream(resource.getFile());  
            //2.模板和数据整合  
            JasperPrint jasperPrint = JasperFillManager  
                    .fillReport(fis, params, connection);  
  
            //3.导出PDF  
            File outFile = new File(filePath);  
            // 文件不存在,且创建失败,则直接返回  
            if (!outFile.exists() && !outFile.createNewFile()) {  
                LOGGER.error("file create failed. ");  
                return;            }  
            fileOutputStream = new FileOutputStream(outFile);  
            JasperExportManager.exportReportToPdfStream(jasperPrint, fileOutputStream);  
  
        } catch (JRException | IOException e) {  
            e.printStackTrace();  
        } finally {  
            // 关闭文件流  
            if (fileOutputStream != null) {  
                try {  
                    fileOutputStream.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
            if (fis != null) {  
                try {  
                    fis.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
  
  
    /**  
     * 导出报表为 pdf  
     * @param templateName ireporter工具生成的模板名称,使用全路径  
     * @param params 报告模板中定义的参数列表  
     * @param fileName 导出的html文件名称,使用全路径。  
     */  
    public void processReporter2Html(String templateName, Map<String, Object> params, String fileName) {  
  
        // 1. 获取模板  
        Resource resource = new ClassPathResource(templateName);  
        FileInputStream fis = null;  
  
        // 2. 获取数据库连接  
        Connection connection = ConnectionProvider.getConnection();  
  
        try {  
            fis = new FileInputStream(resource.getFile());  
  
            // 3. 获取报告对象  
            JasperPrint jasperPrint = JasperFillManager.fillReport(fis, params, connection);  
  
            // 4. 导出至html文件  
            JasperExportManager.exportReportToHtmlFile(jasperPrint, fileName);  
        } catch (IOException | JRException e) {  
            e.printStackTrace();  
        }  
        finally {  
            if (fis != null) {  
                try {  
                    fis.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }  
}

Service层

@Service  
public class JasperReporterServiceImpl implements JasperReporterService {  
  
    @Autowired  
    private JasperReporterHandle jasperReporterHandle;  
  
    // todo 换成自己的路径
    private static final String FILE_PATH = "C:/temp/jsreporter/";  
  
    public void processReport2PDF(Map<String, Object> param, String fileName) {  
  
        // 模板名称  
        String templateName = "static/data_center_billing_mail.jasper";  
  
        String fileFullPath = FILE_PATH + fileName;  
  
        jasperReporterHandle.processReporter2PDF(templateName, param, fileFullPath);  
  
    }  
  
    @Override  
    public void processReport2Html(Map<String, Object> param, String fileName) {  
  
        // 模板名称  
        String templateName = "static/data_center_billing_mail.jasper";  
  
        String outFileName = FILE_PATH + fileName;  
  
        jasperReporterHandle.processReporter2Html(templateName, param, outFileName);  
    }  
}

controller 层

@Controller  
@RequestMapping(value = "/test")  
public class TestController {  
  
  
    @Autowired  
    private JasperReporterService jasperReporterService;  
  
    @GetMapping("processReport2PDF/{projectId}")  
    @ResponseBody  
    public String processReport2PDF(@PathVariable("projectId") Integer projectId){  
  
        Map<String, Object> param = new HashMap<>();  
        param.put("projectId", projectId);  
  
        jasperReporterService.processReport2PDF(param, "resource-reporter-by-project.pdf");  
        return "OK";  
    }  
  
    @GetMapping("/processReport2Html/{projectId}")  
    @ResponseBody  
    public String processReport2Html(@PathVariable("projectId") Integer projectId) {  
  
        Map<String, Object> param = new HashMap<>();  
        param.put("projectId", projectId);  
  
        jasperReporterService.processReport2Html(param, "resource-reporter-by-project.html");  
  
        return "OK";  
    }  
  
}

测试

  1. 启动服务,浏览器访问地址。

Springboot 整合 ireporter 实践
2. 查看生成的文件
Springboot 整合 ireporter 实践

resource-reporter-by-project.html_files中存放的是表格对应的图片。

访问生成的html文件如下图。
Springboot 整合 ireporter 实践

结语

使用ireporter的一个缺点是,模板文件需要提前准备,虽然可以通过提供上传页面来支持,但是导致的结果是需要在两个页面之间切换。如果只是少数几个人使用,可以保持这种模式没问题。如果使用的人数较多,可以考虑对ireporter进行二次开发,提供在线模式,并将生成的模板放到云服务器上。这样其他应用可以直接读取云服务上的模板文件,而免去在应用之间的跳转。