文件上传与PDF报表入门

文件上传与PDF报表入门

理解DataURL的基本使用,实现DataURL的文件上传完成基于七牛云的文件上传

理解 JasperReport生命周期

独立完成 JasperReport的入门案例

图片上传

需求分析

文件上传与PDF报表入门_第1张图片

如图所示,实现员工照片上传功能

Data URL

DataURL概述

所谓DataURL是指"data"类型的Url格式,是在RFC2397中提出的,目的是对于一些“小”的数据,可以在网页中直接嵌入,而不是从外部文件载入。

Data URL入门

完整的DataURL语法:DataURL= data:mediatype;base64,

mediatype:表述传递的数据的MIME类型(text/html,image/png,image/jpg)

骚戴理解:mediatype;和base64是可选的!

简单的说,data类型的Url大致有下面几种形式。

data:,<文本数据>
data:text/plain,<文本数据>
data:text/html,
data:text/html;base64,
data:text/css,
data:text/css;base64,
data:text/javascript,
data:text/javascript;base64,
data:image/gif;base64,base64编码的gif图片数据
data:image/png;base64,base64编码的png图片数据
data:image/jpeg;base64,base64编码的jpeg图片数据
data:image/x-icon;base64,base64编码的icon图片数据

对于再程序开发中,使用最多的是基于DataURL的图片形式,接下来以图片形式的DataURL分析其原理和利弊

Data URL基本原理

Data URL给了我们一种很巧妙的将图片“嵌入”到HTML中的方法。跟传统的用 img 标记将服务器上的图片引用到页面中的方式不一样,在Data URL协议中,图片被转换成base64编码的字符串形式,并存储在URL中,冠以mime- type

图片在网页中的使用方法通常是下面这种利用img标记的形式:

这种方式中,img标记的属性指定了一个远程服务器上的资源。当网页加载到浏览器中时,浏览器会针对每个外部资源都向服务器发送一次拉取资源请求,占用网络资源。大多数的浏览器都有一个并发请求数不能超过4个的限制。这意味着,如果一个网页里嵌入了过多的外部资源,这些请求会导致整个页面的加载延迟。而使用Data URL技术,图片数据以base64字符串格式嵌入到了页面中,与HTML成为一体,它的形式如下:

data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAALwAAAA5CAYAAACMNEHAAAAAGXRFWHRTb2Z0d2FyZ
QBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA4RpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdp
bj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU
6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNS1jMDE0IDc5LjE1MTQ4MSwgMjAxMy8wMy8xMy
0xMjowOToxNSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyL
zIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0
dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3h
hcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC
8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDo5ZDAxYjE4NC04MTFlLTE4NDEtYTUwYi0wMzljN
jZmNDA1YWUiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MUYyRjEwQUFGMEY3MTFFN0IzQUU5ODg2NUEzREJF
MDMiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MUYyRjEwQTlGMEY3MTFFN0IzQUU5ODg2NUEzREJFMDMiIHh
tcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTcgKFdpbmRvd3MpIj4gPHhtcE1NOkRlcml2ZW
RGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6ZDA1ZmRhNzAtMzY1Yi1iNTQwLWE3OTYtOGVjNzJkYTQwZ
mMyIiBzdFJlZjpkb2N1bWVudElEPSJhZG9iZTpkb2NpZDpwaG90b3Nob3A6MDM4NzM5MTYtZTQ4ZS0xMWU3LWFl
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
O34QAAAABJRU5ErkJggg==

上面的base64字符串中你看不出任何跟图片相关的东西,但下面,我们将传统的 img 写法和现在的Data URL用法左右对比显示,你就能看出它们是完全一样的效果。但实际上它们是不一样的,它们一个是引用了外部资源,一个是使用了Data URL。


    

普通URL


Data URL

骚戴理解:可以通过代码看到普通的url是把图片资源放到了服务器上面,然后通过发请求的方式去获取图片资源再显示的页面上面,图片资源通常是转化为二进制或者字节流来进行展示在页面的,Data url是把图片资源通过base64编码的方式变成一长串的字符串,也就是Data url的这一长串字符串其实就是图片资源,不过通过base64加密后变成了字符串,原本是二进制或字节流,这就导致Data url占用的数据容量比普通url更大一些,所以Data url也被叫做带有数据的url!

优缺点分析

  • 浏览器支持

几乎所有的现代浏览器都支持Data URL格式,包括火狐浏览器,谷歌浏览器,Safari浏览器,opera浏览器。IE8也支持,但有部分限制,IE9完全支持。

  • 数据容量

Base64编码的数据体积是原数据的体积4/3,也就是DataURL形式的图片会比二进制格式的图片体积大1/3。

  • 使用场景

DataURL形式的数据不会占用HTTP会话,所以再访问外部资源或当图片是在服务器端用程序动态生成时借用DataURL是一个不错的选择

骚戴理解:普通url是要发http请求的,这就会占用网络资源,如果图片很多的话那就会导致图片加载速度太慢,所以在图片资源所占的数据容量不大的情况下,可以用data url,因为data url本身就包含图片资源,但是如果图片资源所占的数据容量大的情况下,还是普通的url更好

Data URL实现用户头像上传

  • 修改用户实体类,用户数据库表添加用户头像字段
    • 使用基于Data URL的方式实现用户上传,实质是将前端上传的文件以Base64进行编码并且保存到数据库中。用户controller中添加用户上传方法
  • 用户service中添加上传文件处理的方法
    • 在service中需要对文件进行base64编码,并且保存到数据库中
  • 导入imgUpload.vue到src\module-employees\components\component里面
  • 在User实体类中添加private String staffPhoto; //用户头像
  • 在系统微服务的UserController中添加上传处理的方法
    @RequestMapping(value="/user/upload/{id}")
    public Result upload(@PathVariable String id,@RequestParam(name = "file") MultipartFile file) throws Exception {
        String image = userService.uploadImage(id, file);
        return new Result(ResultCode.SUCCESS,image);
   }

骚戴理解:这里我经常犯错误,我一直觉得处理器的参数和请求路径一一对应,其实是错误的,应该是和前端传过来的数据一一对应,例如这里前端传了id和图片的二进制流,那么就处理器就对应着有这两个参数,同时注意图片资源在后端用到的注解是@RequestParam,不是@RequestBody!!!

  • 在系统微服务的UserService中添加上传处理的方法
    public String uploadImage(String id, MultipartFile file) throws Exception {
        //根据id查询用户
        User user = userDao.findById(id).get();
        //对上传文件进行Base64编码
        String s = Base64.encode(file.getBytes());
        //拼接DataURL数据头
        String dataUrl = new String("data:image/jpg;base64,"+s);
        user.setStaffPhoto(dataUrl);
        //保存图片信息
        userDao.save(user);
        return dataUrl;
   }

骚戴理解: Base64在导包的时候不要导错包了!应该导下面这个路径的包

不过可以看到这个路径已经被淘汰了!但是用也可以用咯!

  • 在UserResult里添加private String staffPhoto; 属性,这是用于回显图片的

骚戴理解:这里用的data url的方法来实现图片,并且可以把生成的data url放在数据库里,这个staff_photo就是存储data url的字段,可以看到数据类型是mediumtext,可以看到这样的话数据库内存就更大了,这也是data url最大的缺点!所以data url只适合小型图片,也就是存储容量比较小的图片!像那种几十M的图片就不能用data url!这样的大型图片可以用像七牛云这样的第三方云存储来存储图片!

文件上传与PDF报表入门_第2张图片

解释一下mediumtext:在MySQL中,mediumtext是一种数据类型,用于存储medium长度的文本字符数据,最大长度为16,777,215个字符(约16MB)。与其他文本类型text和longtext相比,mediumtext的长度介于它们之间。适用于需要存储较长的文本数据但又不至于过于庞大的情况。例如存储网页内容或较长的文章文本等。

需要注意的是,由于mediumtext类型允许存储非常大的文本值,因此对于包含大量文本记录的表,查询和写入速度可能会变慢,并且也会占据大量的存储空间。选择合适的文本类型并合理使用数据库索引可以提高查询效率,减小数据存储空间的使用。

七牛云存储

概述

七牛云对象存储服务提供高可靠、强安全、低成本、可扩展的非结构化数据的存储服务。它提供简单的 Web 服务接口,可以通过七牛开发者平台或客户端存储和检索任意数量的数据,支持 “按使用付费” 模式,可以通过调用REST API 接口和 SDK开发工具包访问,下载协议采用HTTP 和 HTTPS 协议。方便程序员聚焦业务应用,而无需关注底层存储实现技术。

使用七牛云实现图片存储也比较简单只需要按照如下的步骤操作即可:

  • 申请七牛云账号
  • 创建空间 Bucket
  • 上传文件
  • 请求获取图片

账户申请

  • 进入七牛云官方网站注册开发者账户

七牛云是通过邮箱注册的,注册激活后就进行认证,认证后即可开通对象存储业务了

文件上传与PDF报表入门_第3张图片

  • 创建存储空间 Bucket

点击左侧左侧菜单对象存储,一开始我们需要新建一个存储空间来存放我们的图片资源。点击新建存储空间,设置一些需要的内容,然后在左侧的存储空间列表我们就可以看到新加的空间了。

文件上传与PDF报表入门_第4张图片

账号注册有些需要注意的点如下:

注册账号之后需要实名认证(个人/企业) 实名认证之后才可以创建存储空间

存储空间创建成功之后,找到个人中心获取accessKey,secretKey和存储空间名称就可以进行上传操作了

入门案例

七牛对象存储将数据文件以资源的形式上传到空间中。可以创建一个或者多个空间,然后向每个空间中上传一个或 多个文件。通过获取已上传文件的地址进行文件的分享和下载

搭建环境


  com.qiniu
  qiniu-java-sdk
  [7.2.0, 7.2.99]

文件上传

    @Test
    public void testUploadImage() {
        Configuration cfg = new Configuration(Zone.zone0());
        UploadManager uploadManager = new UploadManager(cfg);
        String accessKey = "COuoDRVa7JLsuurzIvQSI_pEDceHDw3yGfJEmvwv";
        String secretKey = "3RWpTjB5Jxg3QosUFr4mxbHXJ5JR2m6AHQqYsSlr";
        String bucket = "test-bucket";
        String localFilePath = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day9\\资源\\照片\\001.png";
        //默认不指定key的情况下,以文件内容的hash值作为文件名
        String key = "test";
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        try {
            Response response = uploadManager.put(localFilePath, key, upToken);
            //解析上传成功的结果
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            System.out.println(response.bodyString());
       } catch (QiniuException ex) {
            Response r = ex.response;
            System.err.println(r.toString());
            try {
                System.err.println(r.bodyString());
           } catch (QiniuException ex2) {
                //ignore
           }
       }
   }

骚戴理解:覆盖上传只需要把代码改成下面 String upToken = auth.uploadToken(bucket,key);即可,七牛云有保护机制,

断点续传

    @Test
    public void testUploadImage1() {
        Configuration cfg = new Configuration(Zone.zone0());
        String accessKey = "COuoDRVa7JLsuurzIvQSI_pEDceHDw3yGfJEmvwv";
        String secretKey = "3RWpTjB5Jxg3QosUFr4mxbHXJ5JR2m6AHQqYsSlr";
        String bucket = "test-bucket";
        String key = "test";
        Auth auth = Auth.create(accessKey, secretKey);
        String upToken = auth.uploadToken(bucket);
        String localFilePath = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day9\\资源\\test.xlsx";
       
        //断点续传
        String localTempDir = Paths.get(System.getProperty("java.io.tmpdir"), bucket).toString();
        System.out.println(localTempDir);
        try {
            //设置断点续传文件进度保存目录
            FileRecorder fileRecorder = new FileRecorder(localTempDir);
            UploadManager uploadManager = new UploadManager(cfg, fileRecorder);
            try {
                Response response = uploadManager.put(localFilePath, key, upToken);
                //解析上传成功的结果
                DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
                System.out.println(putRet.key);
                System.out.println(putRet.hash);
           } catch (QiniuException ex) {
                Response r = ex.response;
                System.err.println(r.toString());
                try {
                    System.err.println(r.bodyString());
               } catch (QiniuException ex2) {
                    //ignore
               }
           }
       } catch (IOException ex) {
            ex.printStackTrace();
       }
   }

骚戴理解:七牛云的断点续传是一种传输文件的技术,可以自动从上传失败或中断处重新开始上传,并继续上次未完成的传输操作。这个功能可以很好地解决大文件上传时网络不稳定、上传速度慢等问题而导致的上传失败等情况,提高了文件上传的成功率和效率。实现原理其实就是通过临时文件的形式,把已经上传的内容记录下来,包括位置,下次再从这个位置继续上传!适合传输大文件!

文件下载

对于公开空间,其访问的链接主要是将空间绑定的域名(可以是七牛空间的默认域名或者是绑定的自定义域名)拼接上空间里面的文件名即可访问,标准情况下需要在拼接链接之前,将文件名进行urlencode符。

七牛云实现用户头像上传

  • 在ihrm_common微服务里导入依赖,也可以在父pom里导入这两个依赖
 
        com.qiniu
        qiniu-java-sdk
        [7.2.0, 7.2.99]
    
    
        com.google.code.gson
        gson
        2.8.9
    
  • 创建文件上传的工具类
package com.ihrm.system.utils;

import com.google.gson.Gson;
import com.qiniu.common.Zone;
import com.qiniu.http.Response;
import com.qiniu.storage.Configuration;
import com.qiniu.storage.UploadManager;
import com.qiniu.storage.model.DefaultPutRet;
import com.qiniu.util.Auth;

import java.util.Date;

public class QiniuUploadUtil {

    private static final String accessKey = "COuoDRVa7JLsuurzIvQSI_pEDceHDw3yGfJEmvwv";
    private static final String secretKey = "3RWpTjB5Jxg3QosUFr4mxbHXJ5JR2m6AHQqYsSlr";
    //bucket是指的七牛云存储空间的名称
    private static final String bucket = "ihrm-bucket";
    //七牛云存储空间生成的域名地址
    private static final String prix = "http://pkbivgfrm.bkt.clouddn.com/";
    private UploadManager manager;

    public QiniuUploadUtil() {
        //初始化基本配置
        Configuration cfg = new Configuration(Zone.zone0());
        //创建上传管理器
        manager = new UploadManager(cfg);
    }

    //文件名 = key
    //文件的byte数组
    public String upload(String imgName , byte [] bytes) {
        Auth auth = Auth.create(accessKey, secretKey);
        //构造覆盖上传token
        String upToken = auth.uploadToken(bucket,imgName);
        try {
            Response response = manager.put(bytes, imgName, upToken);
            DefaultPutRet putRet = new Gson().fromJson(response.bodyString(), DefaultPutRet.class);
            //返回请求地址
            return prix+putRet.key+"?t="+new Date().getTime();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }
}

骚戴理解:这个工具类只需要修改accessKey、secretKey、bucket、prix这四个参数即可,然后这里用的是覆盖上传,调用上传方法会返回一个能访问的url地址,也就是下面这行代码

//例如http://ruya1a2n3.hd-bkt.clouddn.com/1659438854295257088?t=1684575706082
return prix+putRet.key+"?t="+new Date().getTime();

这个地址是由七牛云存储空间生成的域名地址(30天后会过期)+文件名称+时间戳组成的,这个时间戳是因为七牛云有缓存机制,你修改图片后可以还是用的缓存里的图片,加个时间戳就可以解决这个问题!还要注意一个点,就是存储空间的地区变了那么下面的这个Zone.zone0()代码也要变,Zone.zone0()指的是华东地区的,七牛云Java SDK还支持其他几个基础API服务地区和区域配置,如下所示:

  • Zone.zone0():华东
  • Zone.zone1():华北
  • Zone.zone2():华南
  • Zone.zoneNa0():北美
  • Zone.zoneAs0():新加坡

使用七牛云实现用户头像上传

修改UserService方法

public String uploadImage(String id, MultipartFile file) throws Exception {
    User user = userDao.findById(id).get();
    String key = new QiniuUploadUtil().upload(user.getId(), file.getBytes());
    if(key != null) {
        user.setStaffPhoto(key);
        userDao.save(user);
   }
    return key;
}

骚戴理解:这里会用QiniuUploadUtil工具类就可,知道upload方法两个参数是什么意思即可,第一个参数是文件名称,这里用的用户id作为文件名,第二个就是图片的二进制流

PDF报表打印概述

概述

在企业级应用开发中,报表生成、报表打印下载是其重要的一个环节。在之前的课程中我们已经学习了报表中比较 重要的一种:Excel报表。其实除了Excel报表之外,PDF报表也有广泛的应用场景,必须用户详细资料,用户简历等。接下来的课程,我们就来共同学习PDF报表

常见PDF报表的制作方式

目前世面上比较流行的制作PDF报表的工具如下:

  • iText PDF:iText是著名的开放项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf
  • 的文档,而且可以将XML、Html文件转化为PDF文件。
  • Openoffice:openoffice是开源软件且能在windows和linux平台下运行,可以灵活的将word或者Excel转化为PDF文档。
  • Jasper Report:是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF

JasperReport框架的介绍

文件上传与PDF报表入门_第5张图片

JasperReport是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF,HTML,或者 XML格式。该库完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。只需要将JasperReport引入工程中即可完成PDF报表的编译、显示、输出等工作。

在开源的JAVA报表工具中,JASPER Report发展是比较好的,比一些商业的报表引擎做得还好,如支持了十字交叉报表、统计报表、图形报表,支持多种报表格式的输出,如PDF、RTF、XML、CSV、XHTML、TEXT、DOCX以及OpenOffice。

数据源支持更多,常用 JDBC SQL查询、XML文件、CSV文件 、HQL(Hibernate查询),HBase,JAVA集合等。还允许你义自己的数据源,通过JASPER文件及数据源,JASPER就能生成最终用户想要的文档格式。

JasperReport的开发步骤

JasperReport生命周期

通常我们提到PDF报表的时候,浮现在脑海中的是最终的PDF文档文件。在JasperReports中,这只是报表生命周期 的最后阶段。通过JasperReports生成PDF报表一共要经过三个阶段,我们称之为 JasperReport的生命周期,这三个阶段为:设计(Design)阶段、执行(Execution)阶段以及输出(Export)阶段,如下图所示:

文件上传与PDF报表入门_第6张图片

  • 设计阶段(Design):所谓的报表设计就是创建一些模板,模板包含了报表的布局与设计,包括执行计算的 复杂公式、可选的从数据源获取数据的查询语句、以及其它的一些信息。模板设计完成之后,我们将模板保存为JRXML文件(JR代表JasperReports),其实就是一个XML文件
  • 执行阶段(Execution):使用以JRXML文件编译为可执行的二进制文件(即.Jasper文件)结合数据进行执 行,填充报表数据
  • 输出阶段(Export):数据填充结束,可以指定输出为多种形式的报表

骚戴理解:这里结合下面的原理进行分析,设计阶段其实就是设计pdf的样式,根据jrxml文件生成jasper文件,执行阶段就是根据jasper文件生成jrprint文件,输出阶段就是根据jrprint生成指定的报表,比如说pdf报表

JasperReport原理

文件上传与PDF报表入门_第7张图片

  • JRXML:报表填充模板,本质是一个XML.
    • JasperReport已经封装了一个dtd,只要按照规定的格式写这个xml文件,那么jasperReport就可以将其解析最终生成报表,但是jasperReport所解析的不是我们常见的.xml文件,而是.jrxml文件,其实跟xml是一样的,只是后缀不一样。
  • Jasper:由JRXML模板编译生成的二进制文件,用于代码填充数据
    • 解析完成后JasperReport就开始编译.jrxml文件,将其编译成.jasper文件,因为JasperReport只可以 对.jasper文件进行填充数据和转换,这步操作就跟我们java中将java文件编译成class文件是一样的
  • Jrprint:当用数据填充完Jasper后生成的文件,用于输出报表
    • 这一步才是JasperReport的核心所在,它会根据你在xml里面写好的查询语句来查询指定是数据库,也可以控制在后台编写查询语句,参数,数据库。在报表填充完后,会再生成一个.jrprint格式的文件(读取jasper文件进行填充,然后生成一个jrprint文件)
    • 骚戴理解:Jrprint是JasperReport报表库所生成的报表文档文件格式之一,它包含了报表生成时使用的所有信息,包括设计好的模板、填充数据、用于输出的控件以及生成的样式等等。因此,Jrprint文件中确实包含了数据填充后的内容。在填充数据之后,JasperReport会将结果存储在内存中,并为每一个页面创建一个对应的子报表元素,最终将这些子报表元素合并到一个整体的Jrprint文件中。需要注意的是,Jrprint文件不同于JasperReport的模板文件(.jrxml),它不能作为原始模板来进行编辑和修改。如果您需要调整报表的外观或逻辑,需要打开对应的.jrxml模板文件,并且重新编译和生成报表。
  • Exporter:决定要输出的报表为何种格式,报表输出的管理类。
    • Jasperreport可以输出多种格式的报表文件,常见的有Html,PDF,xls等

骚戴理解:Jasper文件和Jrprint文件有什么区别?

Jasper文件和Jrprint文件都是JasperReports报表库所使用的文件格式,但它们在生成和使用上有一些区别。

  • Jasper文件(.jasper)是由JasperReports编译器将.jrxml文件(JasperReports模板文件)转换为二进制格式的文件。Jasper文件包含了填充后的数据集、样式、和可视化控件等元素,并且可以作为原始的模板来进行编辑和修改。当需要生成报表时,应通过JasperReports API加载Jasper文件,并为其填充数据集,然后使用所提供的输出组件将其导出为所需的格式(如PDF、HTML、Excel等)。
  • 而Jrprint 文件(.jrprint),则是一个二进制格式的文件,其中包含了通过JasperReports API运行报表后生成的完整报表内容,包括所有页面和分页信息、数据以及渲染样式等。这个文件通常被用于在Java应用程序中查看或打印报表,以便在运行时进行一些更复杂的逻辑处理操作。

总之,Jasper文件主要用于设计与编译报表模板,而Jrprint文件用于展示或处理已经生成的完整报表。需要注意的是,Jasper文件和Jrprint文件都不能直接打开或修改,必须通过相关的JasperReports库来进行操作。

开发流程概述

  • 制作报表模板模板编译
  • 构造数据
  • 填充模板数据

模板工具Jaspersoft Studio

概述

Jaspersoft Studio是JasperReports库和JasperReports服务器的基于Eclipse的报告设计器; 它可以作为Eclipse插件或作为独立的应用程序使用。Jaspersoft Studio允许您创建包含图表,图像,子报表,交叉表等的复杂布局。您可以通过JDBC,TableModels,JavaBeans,XML,Hibernate,大数据(如Hive),CSV,XML / A以及自定义来源等各种来源访问数据,然后将报告发布为PDF,RTF, XML,XLS,CSV,HTML,XHTML,文本,DOCX或OpenOffice。

Jaspersoft Studio 是一个可视化的报表设计工具,使用该软件可以方便地对报表进行可视化的设计,设计结果为格式.jrxml 的 XML 文件,并且可以把.jrxml 文件编译成.jasper 格式文件方便 JasperReport 报表引擎解析、显示。

安装配置

到JasperReport官网下载 Download Jaspersoft Business Intelligence Suite | Jaspersoft Community

文件上传与PDF报表入门_第8张图片

下载 Library Jar包(传统导入jar包工程需下载)和模板设计器Jaspersoft studio。并安装Jaspersoft studio,安装的过程比较简单,一直下一步直至安装成功即可。

骚戴理解:安装下面这个,别安装错了!

面板介绍

文件上传与PDF报表入门_第9张图片

  • Report editing area (主编辑区域)中,您直观地通过拖动,定位,对齐和通过 Designer palette(设计器调色板)对报表元素调整大小。JasperSoft Studio 有一个多标签编辑器,Design,Source 和 Preview:
  • Design tab:当你打开一个报告文件,它允许您以图形方式创建报表选中Source tab: 包含用于报表的 JRXML 源代码。
  • Preview tab: 允许在选择数据源和输出格式后,运行报表预览。
  • Repository Explorer view:包含 JasperServer 生成的连接和可用的数据适配器列表
  • Project Explorer view:包含 JasperReports 的工程项目清单
  • Outline view:在大纲视图中显示了一个树的形式的方式报告的完整结构。
  • Properties view:通常是任何基于 Eclipse 的产品/插件的基础之一。它通常被填充与实际所选元素的属性的信息。这就是这样,当你从主设计区域(即:一个文本字段)选择一个报表元素或从大纲,视图显示了它的 信息。其中一些属性可以是只读的,但大部分都是可编辑的,对其进行修改,通常会通知更改绘制的元素(如:元素的宽度或高度)。
  • Problems view:显示的问题和错误,例如可以阻断报告的正确的编译。
  • Report state summary 提供了有关在报表编译/填充/执行统计用户有用的信息。错误会显示在这里

基本使用

模板制作

  • 打开Jaspersoft Studio ,新建一个project, 步骤: File -> New -> Project-> JasperReports Project

文件上传与PDF报表入门_第10张图片

  • 新建一个Jasper Report模板,在 Stidio的左下方Project Explorer 找到刚才新建的Project (我这里新建的是DemoReport),步骤:项目右键 -> New -> Jasper Report

文件上传与PDF报表入门_第11张图片

  • 选择 Blank A4 (A4纸大小的模板),然后 Next 命名为DemoReport1.jrxml.

文件上传与PDF报表入门_第12张图片

如图所示,报表模板被垂直的分层,每一个部分都是一个Band,每一个Band的特点不同:

文件上传与PDF报表入门_第13张图片

  • Title(标题):只在整个报表的第一页的最上端显示。只在第一页显示,其他页面均不显示
  • Page Header(页头):在整个报表中每一页都会显示。在第一页中,出现的位置在 Title的下面。在除了第一页的其他页面中Page Header 的内容均在页面的最上端显示
  • Page Footer(页脚):在整个报表中每一页都会显示。显示在页面的最下端。一般用来显示页码
  • Detail 1(详细):报表内容,每一页都会显示。
  • Column Header(列头):Detail中打印的是一张表的话,这Column Header就是表中列的列头。Column Footer(列脚):Detail中打印的是一张表的话,这Column Footer就是表中列的列脚。
  • Summary(统计):表格的合计段,出现在整个报表的最后一页中,在Detail1后面。主要是用来做报表的合计显示

骚戴理解:上面的这些都是可以按delete按钮删除的!注意在玩的时候需要先ctrl+s进行保存然后才可以在Preview里面预览到效果!

文件上传与PDF报表入门_第14张图片

编译模板

右键单机模板文件 -> compile Report 对模板进行编译,生成.jasper文件

文件上传与PDF报表入门_第15张图片

骚戴理解:首先在Jaspersoft Studio 里面通过拖拽的方式设计好模板,设计模板的时候其实就是jrxml文件,然后通过编译变成jasper文件,jasper文件才是Java代码里要用到的,后续会把这个jasper文件放在Resource目录下!在Java代码里对jasper文件填充数据生成jrprint文件,然后以pdf报表的形式展示在前端!

文件上传与PDF报表入门_第16张图片

可以看到编译后就会生成jasper文件,然后把这个文件导入项目里即可!

文件上传与PDF报表入门_第17张图片

整合工程

新建SpringBoot工程引入坐标

    
        org.springframework.boot
        spring-boot-starter-parent
        2.0.5.RELEASE
        
    
    
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            net.sf.jasperreports
            jasperreports
            6.5.0
        
        
            org.olap4j
            olap4j
            1.2.0
        
        
            com.lowagie
            itext
            2.1.7
        
        
            org.apache.poi
            poi
            4.0.1
        
        
            org.apache.poi
            poi-ooxml
            4.0.1
        
        
            org.apache.poi
            poi-ooxml-schemas
            4.0.1
        
    

引入配置文件

server:
 port: 8181
spring:
 application:
   name: jasper-demo #指定服务名
 resources:
   static-locations: classpath:/templates/
 datasource:
   driver-class-name: com.mysql.jdbc.Driver
   url: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=utf8
   username: root
   password: 111111

创建启动类

@SpringBootApplication(scanBasePackages = "cn.itcast")
public class JasperApplication {
    public static void main(String[] args) {
        SpringApplication.run(JasperApplication.class, args);
   }
}

导入生成的.jasper文件

创建测试controller

package cn.itcast.controller;

import net.sf.jasperreports.engine.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;

@RestController
public class JasperController {

    @GetMapping("/testJasper")
    public void createPdf(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //1.引入jasper文件
        Resource resource = new ClassPathResource("templates/test.jasper");
        FileInputStream fis = new FileInputStream(resource.getFile());
        //2.创建JasperPrint,向jasper文件中填充数据

        ServletOutputStream os = response.getOutputStream();
        try {
            /**
             * fis: jasper文件输入流
             * new HashMap :向模板中输入的参数
             * JasperDataSource:数据源(和数据库数据源不同)
             *              填充模板的数据来源(connection,javaBean,Map)
             *               填充空数据来源:JREmptyDataSource
             */
            JasperPrint print = JasperFillManager.fillReport(fis, new HashMap<>(),new JREmptyDataSource());
            //3.将JasperPrint已PDF的形式输出
            JasperExportManager.exportReportToPdfStream(print,os);

        } catch (JRException e) {
            e.printStackTrace();
        }finally {
            os.flush();
        }
    }
}

骚戴理解:下面这句代码是把jasper文件进行填充数据,然后生成jrprint对象(文件),注意这三个参数的含义就好

  • fis: jasper文件输入流
  • new HashMap :向模板中输入的参数
  • JasperDataSource:数据源(和数据库数据源不同)
    • 填充模板的数据来源(connection,javaBean,Map)
    • 填充空数据来源:JREmptyDataSource,如果是没有数据源就new JREmptyDataSource()
 JasperPrint print = JasperFillManager.fillReport(fis, new HashMap<>(),new JREmptyDataSource());

还要一行核心代码就是指定通过什么方式把jrprint文件导出,这里指定通过pdf导出流来导出!

JasperExportManager.exportReportToPdfStream(print,os);

中文处理

文件上传与PDF报表入门_第18张图片

  • 通过手动指定中文字体的形式解决中文不现实
    • 添加properties文件:
net.sf.jasperreports.extension.registry.factory.simple.font.families=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory
net.sf.jasperreports.extension.simple.font.families.lobstertwo=stsong/fonts.xml
  • 指定中文配置文件fonts.xml(放Resource目录下)


    
    
    
    
    
    
    
    
    
    
    
    
    
    
        stsong/stsong.TTF
        stsong/stsong.TTF
        stsong/stsong.TTF
        stsong/stsong.TTF
        Identity-H
        true
        
            '华文宋体', Arial, Helvetica, sansserif
            '华文宋体', Arial, Helvetica, sansserif
        
        
    
  • 引入字体库stsong.TTF(放Resource目录下)

你可能感兴趣的:(#,《SaaS人力资源管理系统》,javascript,前端,java)