java实际项目反射、自定义注解的运用实现itext生成PDF的详细应用教程

news/发布时间2024/9/20 8:08:26

开篇引语

小伙伴在学习java是否有这样的困混不知道反射是干嘛的不知道注解有什么用。导致很多人看 java基础的时候迷迷糊糊,那是你还没有在实际项目中遇到,不知道该如何使用它们。接下来我会为你们详细讲解实际项目中是如何运用反射和自定义注解的,不管你现在对反射和注解了解多少,这篇博客站在实际应用角度从业务介绍到代码实现每一步都给出了详细贴图和代码竭力为大家讲解,无论你是否感兴趣你都应该先点赞收藏起来方便以后用到了过来查看。

一、业务场景描述

将14天的气象预报数据以PDF的形式展示,现在已有14天预报的数据,如下。

 要将上面的14条数据放进下面的这个模板中,最终以PDF的形式生成。效果图如下

 二、使用工具准备好模板,最后保存生成PDF

这里需要使用软件工具Adobe Acrobat DC,需要工具的小伙伴可以评论区留言发送:资料。

具体如何使用工具和生成PDF可以看:java使用itex生成PDF-CSDN博客

  

三、代码实现

 实体类展示,这里建议可以大概看一下,先不用看为什么这样设计实体类,以及自定义注解的定义,后面代码具体地方引用了下面的实体类再来看就会清晰很多。

package com.hw.ioz.web.dto;import com.zye.ioz.common.annotations.ReportFiled;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;/*** @author:Ttc* @date 2024/1/22  14:36* @description: 导出大屏预报数据DTO*/
@Data
public class ForecastDataDTO {/*** 日期*/@ApiModelProperty(value = "日期", notes = "")@ReportFiled(value = "date")private String formattedDate;/*** 天气*/@ApiModelProperty(value = "天气", notes = "")@ReportFiled(value = "weather")private String weaDay;/*** 大屏显示风力等级*/@ApiModelProperty(value = "大屏显示风力等级", notes = "")@ReportFiled(value = "wind")private String largeScreenWind;/*** 最高温度*/@ApiModelProperty(value = "最高温度", notes = "")@ReportFiled(value = "tem_max")private String tem1;/*** 最低温度*/@ApiModelProperty(value = "最低温度", notes = "")@ReportFiled(value = "tem_min")private String tem2;
}
package com.hw.ioz.web.dto;import lombok.AllArgsConstructor;
import lombok.Data;/*** @author:Ttc* @date 2024/1/22  15:40*/
@Data
@AllArgsConstructor
public class PdfFiledDto {/*** 文本域内容*/private String val;/*** 字体大小*/private float fontsize;/*** 自定义字体*/private String fontPath;
}
package com.hw.ioz.web.dto;import com.zye.ioz.common.annotations.ReportFiled;
import com.zye.ioz.common.annotations.ReportList;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;import java.util.List;import static com.zye.ioz.common.constants.PdfFontConstants.FONT_MS_YH;/*** @author:Ttc* @date 2024/1/18  17:51* @description: 大屏天气14天预报导出pdf日期显示dto*/@Data
public class ReportDateDTO {/*** 年*/@ApiModelProperty(value = "年", notes = "")@ReportFiled(value = "year", description = "date", font = FONT_MS_YH, fontsize = 13.5f)private String year;/*** 月*/@ApiModelProperty(value = "月", notes = "")@ReportFiled(value = "month", description = "date", font = FONT_MS_YH, fontsize = 13.5f)private String month;/*** 日*/@ApiModelProperty(value = "日", notes = "")@ReportFiled(value = "day", description = "date", font = FONT_MS_YH, fontsize = 13.5f)private String day;@ReportList("report_")private List<ForecastDataDTO> forecastDataDTOList;
}
package com.zye.ioz.common.annotations;import java.lang.annotation.*;/*** @author:Ttc* @date 2024/1/23  14:48* @description: pdf*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ReportList {/*** 报表集合名称* 作为其中元素的前缀* @return*/String value() default "";
}
package com.zye.ioz.common.annotations;import java.lang.annotation.*;/*** @author:Ttc* @date 2024/1/18  17:54* @description: 报告字段*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ReportFiled {String value() default "";float fontsize() default 12.0f;/*** 字体路径* 如果为空,使用默认字体* @return*/String font() default "";String prefix() default "";String description() default "";
}

下面controller层代码调用的方法中传递的参数templatePath为pdf模板的存放路径地址
我是从yml文件中读取的,部分yml展示:

template:weather: D:\lightning2022\templatefont: D:\lightning2022\font\
@Api(tags = "PDF下载")
@RestController
@RequestMapping("/api/wea")
@Slf4j
public class BnWeaInterfaceHistoryController {@Value("${template.weather}")
private String templatePath;@GetMapping("/pdfExport")@ApiOperation(value = "大屏14天预报数据PDF下载")public void parkingPdfExport(HttpServletResponse response) {bnWeaInterfaceHistoryService.exportReport(response,templatePath);}
}

下面这段代码是将处理完成后的地址返回,拿到地址以后,使用字符输入流读取刚刚返回的对应PDF地址的文件,然后再用字节输出流/二进制输出流返回给前端进行下载处理。到此下载功能完成!后面是核心方法实现的详细介绍。

@Service
@Slf4j
public class BnWeaInterfaceHistoryServiceImpl extends ServiceImpl<BnWeaInterfaceHistoryMapper, BnWeaInterfaceHistory> implements BnWeaInterfaceHistoryService {@Value("${template.font}")String fontPath;@Overridepublic void exportReport(HttpServletResponse response, String templatePath) {List<ForecastDataDTO> list = bnWeaInterfaceHistoryMapper.exportLargeScreenForecastData("舟山");LocalDateTime now = LocalDateTime.now();Integer year = now.getYear();Integer month = now.getMonth().getValue();Integer day = now.getDayOfMonth();ReportDateDTO reportDateDTO = new ReportDateDTO();reportDateDTO.setDay(day.toString());reportDateDTO.setMonth(month.toString());reportDateDTO.setYear(year.toString());reportDateDTO.setForecastDataDTOList(list);String tmpPath = generateByTemplate(templatePath, reportDateDTO);if (org.springframework.util.StringUtils.isEmpty(tmpPath)) {log.error("找不到PDF路径!");return;}String path = tmpPath;File file = new File(path);log.info("PDF文件:{}", path);response.reset();response.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");response.setContentLength((int) file.length());response.setHeader("Content-Disposition", "attachment;filename=" + "weather"); // 导出名称try {BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));byte[] buff = new byte[1024];OutputStream os = response.getOutputStream(); // 输出流int i = 0;while ((i = bis.read(buff)) != -1) { // 读取文件os.write(buff, 0, i);os.flush();}log.info("发送完成");} catch (IOException e) {log.error("导出失败", e);}}
}

这里主要完成制作好的PDF模板中文本域所需要的值,这里的值还要经过一系列处理收集到map集合中。而这段主要是在收集完map集合的数据以后,再循环取出map集合中的值,将value中存放的PdfFiledDto对象取出来,把该对象中存放的文本域的值,大小,字体设置到PDF模板中。所有数据设置完成以后方法返回生成的PDF文件的保存地址,供下载使用。

/*** 由模板生成PDF,返回模板文件路径** @return*/private String generateByTemplate(String templatePath, ReportDateDTO reportDateDTO) {String tmpName = "ZSWeather14Days.pdf";String tmpPath = templatePath + "/" + tmpName;String newTmpFileName = PdfUtils.getNewFileName(tmpName);SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");String todayStr = df.format(new Date());// 生成的新文件路径(方法返回的模板路径)String pdfPath = templatePath + "/" + todayStr + "/" + newTmpFileName;String filePath = templatePath + "/" + todayStr;File path = new File(filePath);if (!path.exists()) {path.mkdirs();}log.info("pdf文件路径:{}", filePath);try {//设置中文字体BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);PdfReader reader = new PdfReader(tmpPath);// 读取pdf模板ByteArrayOutputStream bos = new ByteArrayOutputStream();     //获取字节数组输出流PdfStamper stamper = new PdfStamper(reader, bos);  //操作pdf文件AcroFields form = stamper.getAcroFields(); //获取pdf模板中的属性form.addSubstitutionFont(bf);      //为属性设置字体Map<String, PdfFiledDto> data = getParamMap(reportDateDTO, null, 0);for (Map.Entry<String, PdfFiledDto> entry : data.entrySet()) {if (entry.getValue().getFontsize() > 0) {form.setFieldProperty(entry.getKey(), "textsize", entry.getValue().getFontsize(), null);}BaseFont baseFont;if (!org.springframework.util.StringUtils.isEmpty(entry.getValue().getFontPath())) {baseFont = BaseFont.createFont(fontPath + entry.getValue().getFontPath(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);} else {baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);}form.setFieldProperty(entry.getKey(), "textfont", baseFont, null);
//                    form.setFieldProperty(entry.getKey(), "textfont", BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED), null);form.setField(entry.getKey(), entry.getValue().getVal());}stamper.setFormFlattening(true);// 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑stamper.close();reader.close();//生成pdf路径FileOutputStream fos = new FileOutputStream(pdfPath);fos.write(bos.toByteArray());fos.flush();if (fos != null) {fos.close();}if (bos != null) {bos.close();}log.info("pdf模板生成了");} catch (Exception e) {e.printStackTrace();}return pdfPath;}

 这里将查询的数据进行反射处理,经过反射得到数据对象的字段后,再获取每个字段上的自定义注解。

public Map<String, PdfFiledDto> getParamMap(Object param, String prefix, int index) {Map<String, PdfFiledDto> map = new HashMap<>(16);if (param == null) {return map;}Field[] fields = param.getClass().getDeclaredFields();for (Field field : fields) {Object formVal = ReflectUtil.getFieldValue(param, field.getName());ReportFiled reportFiled = field.getAnnotation(ReportFiled.class);if (reportFiled != null) {fillParamMapByReportFiled(map, formVal, reportFiled, prefix, index);}ReportList reportList = field.getAnnotation(ReportList.class);if (reportList != null) {String formKeyPrefix = reportList.value();if (formVal instanceof List) {List<Object> formObjList = (List<Object>) formVal;for (int i = 0; i < formObjList.size(); i++) {Object subObj = formObjList.get(i);Map<String, PdfFiledDto> subMap = getParamMap(subObj, formKeyPrefix, i + 1);map.putAll(subMap);}}}}return map;}private static void fillParamMapByReportFiled(Map<String, PdfFiledDto> map,Object formVal, ReportFiled reportFiled, String prefix, int index) {String formKey = reportFiled.value();if (StrUtil.isNotBlank(prefix)) {// 获取实际keyformKey = handleFormKey(prefix, index, formKey);}if (formVal == null) {map.put(formKey, getPdfFiledDto("", -1.0f));return;}fillParamMapByObject(map, reportFiled, formKey, formVal, index);}

两个自定义注解:

package com.zye.ioz.common.annotations;import java.lang.annotation.*;/*** @author:Ttc* @date 2024/1/18  17:54* @description: 报告字段*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ReportFiled {String value() default "";float fontsize() default 12.0f;/*** 字体路径* 如果为空,使用默认字体* @return*/String font() default "";String prefix() default "";String description() default "";
}

package com.zye.ioz.common.annotations;import java.lang.annotation.*;/*** @author:Ttc* @date 2024/1/23  14:48* @description: pdf*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ReportList {/*** 报表集合名称* 作为其中元素的前缀* @return*/String value() default "";
}

 具体的实现方法代码如下,这里的代码完成了PDF中需要数据的查询,以及数据设置到对应的map集合中使得,map集合数据的key对应PDF模板设置的文本域的名称,value则是我们提前查好的数据的值。 

/*** 解析注解中的内容,构造 PdfFiledDto* 并添加进map** @param map* @param reportFiled* @param formKey* @param item*/private static void fillParamMapByObject(Map<String, PdfFiledDto> map, ReportFiled reportFiled,String formKey, Object item, int index) {
//        formKey = formKey + StrPool.UNDERLINE + index;if (item == null || StrUtil.isEmpty(item.toString())) {// 没有数据时,显示 "/"map.put(formKey, getPdfFiledDto("/", reportFiled.fontsize()));} else if ("date".equals(reportFiled.description())) {map.put(formKey, getPdfFiledDto(reportFiled.prefix() + item, reportFiled.fontsize(), reportFiled.font()));} else {map.put(formKey, getPdfFiledDto(reportFiled.prefix() + item, reportFiled.fontsize(), reportFiled.font()));}}private static String handleFormKey(String prefix, int index, String formKey) {prefix = prefix.endsWith(StrPool.UNDERLINE) ? prefix : prefix + StrPool.UNDERLINE;return StrUtil.isBlank(formKey) ?prefix + index: prefix + formKey + StrPool.UNDERLINE + index;}private static PdfFiledDto getPdfFiledDto(String val, float fontsize) {return new PdfFiledDto(val, fontsize, null);}private static PdfFiledDto getPdfFiledDto(String val, float fontsize, String fontPath) {return new PdfFiledDto(val, fontsize, fontPath);}

 来看看运行后的下载结果吧

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.bcls.cn/ombw/9850.shtml

如若内容造成侵权/违法违规/事实不符,请联系编程老四网进行投诉反馈email:xxxxxxxx@qq.com,一经查实,立即删除!

相关文章

通过一篇文章让你了解数据结构和算法的重要性

通过一篇文章让你了解数据结构和算法的重要性 前言一、 什么是数据结构&#xff1f;二、什么是算法&#xff1f;三、数据结构和算法的重要性在校园招聘的笔试中&#xff1a;在校园招聘的面试中&#xff1a;在未来的工作中&#xff1a; 四、如何学好数据结构和算法4.1 死磕代码&…

持安科技亮相张江高科895创业营,总评分第三名荣获「最具创新性企业」!

近日&#xff0c;张江高科895创业营&#xff08;第十三季&#xff09;信息安全专场Demo day&结营仪式在上海集成电路设计产业园圆满落幕。本季创业营通过多种渠道在海内外甄选优秀创业项目&#xff0c;一共择优录取了29家入营&#xff0c;最终甄选出9家代表参加Demo day路演…

如何在Window系统部署BUG管理软件并结合内网穿透实现远程管理本地BUG

文章目录 前言1. 本地安装配置BUG管理系统2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射本地服务3. 测试公网远程访问4. 配置固定二级子域名4.1 保留一个二级子域名5.1 配置二级子域名6. 使用固定二级子域名远程 前言 BUG管理软件,作为软件测试工程师的必备工具之一。在…

科大睿智祝贺企业高标准高质量完成CMMI3级评估认证

航天万源实业有限公司是中国航天科技集团有限公司第一研究院的全资子公司&#xff0c;公司现有从业人员1400余人&#xff0c;年产值30亿元。是经北京市丰台区科学技术委员会审核的高新技术企业。公司践行“服务航天、保障科研”的使命&#xff0c;依托中国航天强大的科技优势、…

buuctf_misc_九连环

题目&#xff1a;&#xff08;一张123456cry.jpg&#xff09; 这个先直接上kali&#xff0c;图片已改名cry.jpg 在上一篇&#xff0c;我留存了kali文件夹下有"叉"打不开的问题&#xff0c;经查阅&#xff0c;已解决&#xff1a; http://t.csdnimg.cn/bgv4T 输入&a…

ref和reactive用哪个?

ref和reactive用哪个? 1.&#x1f916;GPT&#x1f916;:ref和reactive用哪个根据数据类型而定 ref 用于将基本类型的数据&#xff08;如字符串、数字&#xff0c;布尔值等&#xff09;转换为响应式数据。使用 ref 定义的数据可以通过 .value 属性访问和修改。 reactive 用于…

Windows Docker 部署 Redis

部署 Redis 打开 Docker Desktop&#xff0c;切换到 Linux 内核。然后在 PowerShell 执行下面命令&#xff0c;即可启动一个 redis 服务。这里安装的是 7.2.4 版本&#xff0c;如果需要安装其他或者最新版本&#xff0c;可以到 Docker Hub 中进行查找。 docker run -d --nam…

MySQL数据库下载及安装教程(最最新版)

MySQL数据库下载及安装教程&#xff08;最最新版&#xff09; 一、下载mysql数据库二、安装Mysql三、验证是否安装成功&#xff08;一&#xff09;、命令提示符cmd窗口验证&#xff08;二&#xff09;、MySQL控制台验证 一、下载mysql数据库 进入MySQL官方网站&#xff08;htt…

VR虚拟现实技术应用到猪抗原体检测的好处

利用VR虚拟仿真技术开展猪瘟检测实验教学确保生猪产业健康发展 为了有效提高猪场猪瘟防控意识和检测技术&#xff0c;避免生猪养殖业遭受猪瘟危害&#xff0c;基于VR虚拟仿真技术开展猪瘟检测实验教学数据能大大推动基层畜牧养殖业持续稳步发展保驾护航。 一、提高实验效率 VR虚…

LeetCode -- 131.分割回文串

1. 问题描述 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。回文串 是正着读和反着读都一样的字符串。 示例 1&#xff1a; 输入&#xff1a;s “aab” 输出&#xff1a;[[“a”,“a”,“b”],[“aa…

网络爬虫的危害,如何有效的防止非法利用

近年来&#xff0c;不法分子利用“爬虫”软件收集公民隐私数据案件屡见不鲜。2023年8月23日&#xff0c;北京市高级人民法院召开北京法院侵犯公民个人信息犯罪案件审判情况新闻通报会&#xff0c;通报侵犯公民个人隐私信息案件审判情况&#xff0c;并发布典型案例。在这些典型案…

Centos中安装Docker及Docker的使用

在centos7系统中安装指定版本的docker,并通过docker使用安装mysql为例,阐述docker的使用。 2.1、Docker卸载及安装yum依赖 【卸载Docker,如果安装的Docker的版本不合适】 yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-…

使用Node.js构建一个简单的聊天机器人

当谈到人工智能&#xff0c;我们往往会想到什么&#xff1f;是智能语音助手、自动回复机器人等。在前端开发领域中&#xff0c;我们也可以利用Node.js来构建一个简单而有趣的聊天机器人。本文将带你一步步实现一个基于Node.js的聊天机器人&#xff0c;并了解其工作原理。 首先…

Vue源码系列讲解——实例方法篇【一】(数据相关方法)

目录 0. 前言 1. vm.$watch 1.1 用法回顾 1.2 内部原理 2. vm.$set 2.1 用法回顾 2.2 内部原理 3. vm.$delete 3.1 用法回顾 3.2 内部原理 0. 前言 与数据相关的实例方法有3个&#xff0c;分别是vm.$set、vm.$delete和vm.$watch。它们是在stateMixin函数中挂载到Vue原…

德人合科技 | —数据泄露可能会对公司造成哪些影响?

数据泄露可能会对公司造成多方面的影响&#xff0c;以下是一些可能的影响&#xff1a; 财务损失&#xff1a;数据泄露可能导致公司遭受财务损失。攻击者可能会盗取公司的敏感信息&#xff0c;如客户信息、银行账户信息、商业机密等&#xff0c;并利用这些信息进行欺诈、盗窃等非…

用户案例|GreptimeDB 助力贵州某机场智慧能源物联网系统

近年来&#xff0c;云计算和物联网技术的飞速发展促使许多传统单位的用电、用能系统向数字化、信息化、智能化的方向迈进&#xff0c;旨在实现全过程的实时智能协同&#xff0c;提高生产效率。而随着电力采集、监测数据功能的不断增强&#xff0c;数据量也在不断增加&#xff0…

OpenHarmony 串口服务访问

项目介绍 本文档是在eTS项目hap包中实现串口访问的使用说明&#xff0c;通过JS接口开放给上层应用使用。 一、开发环境准备 安装OpenHarmony SDK 1. 在DevEco Studio菜单栏选择Tools->SDK Manager 2. OpenHarmony SDK选项中选择配备API版本进行安装 二、创建eTS项目 创…

springcloud alibaba组件简介

一、Nacos 服务注册中心/统一配置中心 1、介绍 Nacos是一个配置中心&#xff0c;也是一个服务注册与发现中心。 1.1、配置中心的好处&#xff1a; &#xff08;1&#xff09;配置数据脱敏 &#xff08;2&#xff09;防止出错&#xff0c;方便管理 &#xff08;3&#xff…

前端开发——ElementUI组件的使用

文章目录 1. Tabs标签页2. 单选框 el-radio3. 复选框 el-checkbox4. 下拉框 el-select5. 表格 el-table6. 对话框 el-dialog7. 文字提示 el-tooltip8. 抽屉 el-drawer 1. Tabs标签页 <template><el-tabs v-model"activeName" tab-click"handleClick&q…

AI智能分析网关V4:抽烟/打电话/玩手机行为AI算法及场景应用

抽烟、打电话、玩手机是人们在日常生活中常见的行为&#xff0c;但这些行为在某些场合下可能会带来安全风险。因此&#xff0c;对于这些行为的检测技术及应用就变得尤为重要。今天来给大家介绍一下TSINGSEE青犀AI智能分析网关V4抽烟/打电话/玩手机检测算法及其应用场景。 将监控…
推荐文章