所谓DataURL是指"data"类型的Url格式,是在RFC2397中提出的,目的是对于一些“小”的数据,可以在网页中直接嵌入,而不是从外部文件载入。
简单的说,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图片数据
对于在程序开发中,使用最多的是基于Data URL的图片形式,接下来以图片形式的Data URL分析原理和优缺点。
Data URL给了我们一种很巧妙的将图片“嵌入”到HTML中的方法。跟传统的用 img 标记将服务器上的图片引用到页面中的方式不一样,在Data URL协议中,图片被转换成base64编码的字符串形式,并存储在URL中,冠以mimetype。
图片在网页中的使用方法通常是下面这种利用img标记的形式:
<img src="images/myimage.gif ">
这种方式中,img标记的src属性指定了一个远程服务器上的资源。当网页加载到浏览器中时,浏览器会针对每个外部资源都向服务器发送一次拉取资源请求,占用网咯资源。大多数的浏览器都有一个并发请求数不超过4个的限制。这也就意味着,如果一个网页里嵌入了过多的外部资源,这些请求会导致整个页面的加载延迟,而使用Data URL技术,图片数据以base64字符串格式嵌入到了页面中,与HTML成为一体。
普通的URL和Data URL展示出的效果我们观察的话没有什么区别,但实际上它们是不一样的,一个是引用了外部资源,另一个是使用了Data URL。
1、在系统微服务的UserController中添加上传文件处理的方法
/**
* 完成用户头像上传
*/
@RequestMapping(value = "/user/upload/{id}")
public Result upload(@PathVariable String id, @RequestParam("file") MultipartFile file) throws IOException {
//调用service保存图片(获取到图片的访问地址(dataUrl | http地址))
String imgUrl = userService.uploadImage(id, file);
//返回数据
return new Result(ResultCode.SUCCESS, imgUrl);
}
2、在系统微服务的UserService中添加上传文件处理的方法
/**
* 完成用户处理
* @param id 用户id
* @param file 用户上传的头像文件
* @return 请求路径
*/
public String uploadImage(String id, MultipartFile file) throws IOException {
//1、根据id查询用户
User user = userDao.findById(id).get();
//2、使用DataURL形式存储图片(对图片byte数组进行base64编码)
String encode = "data:image/png;base64," + Base64.encode(file.getBytes());
//3、更新用户头像
user.setStaffPhoto(encode);
userDao.save(user);
//4、返回结果
return encode;
}
七牛云对象存储服务提供高可靠、强安全、低成本、可扩展的非结构化数据的存储服务。它提供简单的Web 服务接口,可以通过七牛开发者平台或客户端存储和检索任意数量的数据,支持 “按使用付费” 模式,可以通过调用REST API 接口和 SDK开发工具包访问,下载协议采用HTTP 和 HTTPS 协议。方便程序员聚焦业务应用,而无需关注底层存储实现技术。
使用七牛云实现图片存储也比较简单只需要按照如下的步骤操作即可:
1、申请七牛云账号
2、创建控件bucket
3、上传文件
4、请求获取图片
1、进入官网进行用户注册即可
2、传播构建存储空间bucket
账号注册需要注意的点如下:
创建一个maven工程,导入相关依赖
<dependency>
<groupId>com.qiniugroupId>
<artifactId>qiniu-java-sdkartifactId>
<version>[7.2.0, 7.2.99]version>
dependency>
七牛云官方给我们提供了SDK文档,可以方便我们快速使用。
/**
* 将图片上传到七牛云服务
* 1.更新用户图片信息(用户id=key)
* 2.访问图片
* 存储空间分配的:http://pkbivgfrm.bkt.clouddn.com
* 上传的文件名
* 更新图片之后:访问的时候,在请求连接添加上一个随机字符
*
*/
@Test
public void testUpload01() {
//构造一个带指定Zone对象的配置类
//指定上传文件服务器地址:
Configuration cfg = new Configuration(Zone.zone0());
//上传管理器
UploadManager uploadManager = new UploadManager(cfg);
//生成上传凭证,然后准备上传
String accessKey = "COuoDRVa7JLsuurzIvQSI_pEDceHDw3yGfJEmvwv";
String secretKey = "3RWpTjB5Jxg3QosUFr4mxbHXJ5JR2m6AHQqYsSlr";
String bucket = "ihrm-bucket";
//图片路径
String localFilePath = "IdeaProjects/hrm/qiniudemo/src/poi/1.jpg";
//存入到存储空间的文件名
String key = "test";
//身份认证
Auth auth = Auth.create(accessKey, secretKey);
//指定覆盖上传
String upToken = auth.uploadToken(bucket,key);
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) {
ex2.printStackTrace();
}
}
}
/**
* 断点续传
*/
@Test
public void testUpload02() {
//构造一个带指定Zone对象的配置类
Configuration cfg = new Configuration(Zone.zone0());
//其他参数参考类注释
//生成上传凭证,然后准备上传
String accessKey = "COuoDRVa7JLsuurzIvQSI_pEDceHDw3yGfJEmvwv";
String secretKey = "3RWpTjB5Jxg3QosUFr4mxbHXJ5JR2m6AHQqYsSlr";
String bucket = "ihrm-bucket";
String localFilePath = "IdeaProjects/hrm/qiniudemo/src/poi/test.xlsx";
//默认不指定key的情况下,以文件内容的hash值作为文件名
String key = "testExcel";
Auth auth = Auth.create(accessKey, secretKey);
String upToken = auth.uploadToken(bucket);
//断点续传:
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以兼容不同的字符。
1、在ihrm_common工程下创建文件上传工具类
package com.ihrm.common.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";
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;
}
}
2、修改系统微服务的UserService方法
/**
* 上传到七牛云存储
*/
public String uploadImage(String id, MultipartFile file) throws IOException {
//1、根据id查询用户
User user = userDao.findById(id).get();
//2、将图片上传到七牛云存储,获取请求路径
String imgUrl = new QiniuUploadUtil().upload(id, file.getBytes());
//3、更新用户头像
user.setStaffPhoto(imgUrl);
userDao.save(user);
//4、返回结果
return imgUrl;
}
在企业级应用开发中,报表生成、报表打印下载是其重要的一个环节。在之前我们已经介绍了报表中比较重要的一种:Excel报表。其实除了Excel报表之外,PDF报表也有广泛的应用场景,必须用户详细资料,用户简历等。接下来主要介绍PDF报表。
目前世面上比较流行的制作PDF报表的工具如下:
JasperReport是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF,HTML,或者XML格式。该库完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。只需要将JasperReport引入工程中即可完成PDF报表的编译、显示、输出等工作。
通常我们提到PDF报表的时候,浮现在脑海中的是最终的PDF文档文件。在JasperReports中,这只是报表生命周期的最后阶段。通过JasperReports生成PDF报表一共要经过三个阶段,我们称之为JasperReport的生命周期,这三个阶段为:设计(Design)阶段、执行(Execution)阶段以及输出(Export)阶段,如下图所示:
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官网下载https://community.jaspersoft.com/community-download
下载 Library Jar包(传统导入jar包工程需下载)和模板设计器Jaspersoft studio。并安装Jaspersoft studio,安装的过程比较简单,一直下一步直至安装成功即可。
1、打开Jaspersoft Studio ,新建一个project, 步骤: File -> New -> Project-> JasperReports Project
2、新建一个Jasper Report模板,在 Stidio的左下方Project Explorer 找到刚才新建的Project (我这里新建的是DemoReport),步骤:项目右键 -> New -> Jasper Report
3、选择 Blank A4 (A4纸大小的模板),然后 Next 命名为DemoReport1.jrxml.
如图所示,报表模板被垂直的分层,每一个部分都是一个Band,每一个Band的特点不同:
右键单机模板文件 -> compile Report 对模板进行编译,生成.jasper文件
1、新建SpringBoot工程,引入相关依赖
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.0.5.RELEASEversion>
<relativePath/>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>net.sf.jasperreportsgroupId>
<artifactId>jasperreportsartifactId>
<version>6.5.0version>
dependency>
<dependency>
<groupId>org.olap4jgroupId>
<artifactId>olap4jartifactId>
<version>1.2.0version>
dependency>
<dependency>
<groupId>com.lowagiegroupId>
<artifactId>itextartifactId>
<version>2.1.7version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxml-schemasartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>
dependencies>
2、配置application.yml
server:
port: 8181
#spring配置
spring:
#1.应用配置
application:
name: jasper-springboot #指定服务名
#2.数据库连接池
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/ihrm?useUnicode=true&characterEncoding=utf8
username: xxxx
password: xxxx
3、创建启动类
package cn.itcast;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "cn.itcast")
public class JasperApplication {
/**
* 启动方法
*/
public static void main(String[] args) {
SpringApplication.run(JasperApplication.class,args);
}
}
4、在resources的创建templates目录并导入生成的.jasper文件
5、编写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();
}
}
}
这时候我们预览我们编写文件的效果,发现所有的中文部分全部没有显示出来,这时候我们可以编写插件进行处理这个问题。
1、设计阶段需要指定中文样式
2、手动指定中文字体的形式解决中文不现实
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
<fontFamilies>
<fontFamily name="华文宋体">
<normal>stsong/stsong.TTFnormal>
<bold>stsong/stsong.TTFbold>
<italic>stsong/stsong.TTFitalic>
<boldItalic>stsong/stsong.TTFboldItalic>
<pdfEncoding>Identity-HpdfEncoding>
<pdfEmbedded>truepdfEmbedded>
<exportFonts>
<export key="net.sf.jasperreports.html">'华文宋体', Arial, Helvetica, sans-serifexport>
<export key="net.sf.jasperreports.xhtml">'华文宋体', Arial, Helvetica, sans-serifexport>
exportFonts>
fontFamily>
fontFamilies>