首页 文章 精选 留言 我的

精选列表

搜索[SpringBoot],共4561篇文章
优秀的个人博客,低调大师

MateCloud 4.3.8 正式版发布,更新至 SpringBoot 最新版本

一、发布说明 MateCloud 4.3.8对Spring Boot和Spring Cloud进行一次大版本升级,基于Spring Cloud Alibaba推出的微服务快速开发平台,集成Spring Boot 2.6.4, Spring Cloud 2021.0.1、Nacos 2.0.4、Sentinel 1.8.3、Jetcache等诸多中间件。欢迎体验。 1.1 功能升级 拆分用户按钮权限接口 spring cloud loadbalancer集成caffenine, 增加本地环境变量的配置,支持同机多环境的开发 删除掉暂时不用的加密模块 移除Velocity模板依赖 修复操作日志消费两次的bug 1.2 文档更新 去掉投票地址 1.3 依赖升级 升级至 Spring Boot 2.6.4 升级至 Spring Cloud 2021.0.1 升级至 Mybatis Plus 3.5.1 升级至 Spring Boot Admin 2.6.2 升级至 XXL-JOB 2.3.0 升级至 Postgresql 42.3.3 升级至 Nacos 2.0.4 升级至 Hutool 5.7.19 二、系统演示 2.1 演示地址:http://cloud.mate.vip 账号 密码 操作权限 admin matecloud mate-system模块不能执行增删改请求 如果需要验证手机号码登录,手机号码采用页面默认号码,点击获取验证码,输入1188,即可登录。 2.2 商业版:http://plus.mate.vip 账号 密码 操作权限 admin matecloud123 不能执行增删改请求,如需全部权限加微信 matecloud 联系 2.3 版本演进 核心中间件 2.5.8及以下 3.0.8+ Spring Boot 2.3.*.RELEASE Spring Cloud Hoxton SR* Spring Cloud Alibaba 2.2.*.RELEASE Nacos 1.4.*及以下 Sentinel 1.8.1 2.4 官方文档 2.4.1 文档地址:http://www.mate.vip/docs 2.4.2 商业版文档:http://doc.mate.vip 2.4.3 快速安装:http://www.mate.vip/archives/107 三、 前端重大更新 前端采用Vue3.2、Vite 2.5.*、Ant-Design-Vue 2.*、TypeScript的大型中后台解决方案。 四、 版本发布 4.1.8版本已经发布,实现了系统管理的基础功能,主要包括菜单管理、用户管理、角色管理、部门管理、日志管理、客户端管理等功能。欢迎体验。 五、技术架构 六、 功能特点 主体框架:采用最新的Spring Cloud 2021.0.0,Spring Boot 2.6.1,Spring Cloud Alibaba 2021.1版本进行系统设计; 统一注册:支持Nacos作为注册中心,实现多配置、分群组、分命名空间、多业务模块的注册和发现功能; 统一认证:统一Oauth2认证协议,采用jwt的方式,实现统一认证,并支持自定义grant_type实现手机号码登录,第三方登录集成JustAuth实现微信、支付宝等多种登录模式; 业务监控:利用Spring Boot Admin来监控各个独立Service的运行状态。 内部调用:集成了Feign和Dubbo两种模式支持内部调用,并且可以实现无缝切换,适合新老程序员,快速熟悉项目; 业务熔断:采用Sentinel实现业务熔断处理,避免服务之间出现雪崩; 身份注入:通过注解的方式,实现用户登录信息的快速注入; 在线文档:通过接入Knife4j,实现在线API文档的查看与调试; 代码生成:基于Mybatis-plus-generator自动生成代码,提升开发效率,生成模式不断优化中,暂不支持前端代码生成; 消息中心:集成消息中间件RocketMQ和Kafka,对业务进行异步处理; 业务分离:采用前后端分离的框架设计,前端采用vue-element-admin,商业版采用antd-pro-vue 链路追踪:自定义traceId的方式,实现简单的链路追踪功能 多租户功能:集成Mybatis Plus,实现SAAS多租户功能 七、 项目源码 项目 GITHUB 码云 MateCloud后端源码 https://github.com/matevip/matecloud https://gitee.com/matevip/matecloud Artemis前端源码 https://github.com/matevip/artemis https://gitee.com/matevip/artemis MateBoot后端源码 https://github.com/matevip/mateboot https://gitee.com/matevip/mateboot 7.1 微服务项目示例 https://gitee.com/matevip/mate-demo 八、部分截图 八、下一步更新计划 对uaa认证模块进行重构。敬请期待。

优秀的个人博客,低调大师

【SpringBoot技术专题】「JWT技术专区」SpringSecurity整合JWT授权和认证实现

JWT基本概念 JWT,即 JSON Web Tokens(RFC 7519),是一个广泛用于验证 REST APIs 的标准。虽说是一个新兴技术,但它却得以迅速流行。 JWT的验证过程是: 前端(客户端)首先发送一些凭证来登录(我们编写的是 web 应用,所以这里使用用户名和密码来做验证)。 后端(服务端)这里指Spring应用校验这些凭证,如果校验通过则生成并返回一个 JWT。 客户端需要在请求头的Authorization字段中以 “Bearer TOKEN” 的形式携带获取到的token,服务端会检查这个token是否可用并决定授权访问或拒绝请求。 token中可能保存了用户的角色信息,服务端可以根据用户角色来确定访问权限。 实现 我们来看一下在实际的 Spring 项目中是如何实现JWT登录和保存机制的。 依赖 下面是我们示例代码的 Maven 依赖列表,注意,截图中并未包含Spring Boot、Hibernate等核心依赖(你需要自行添加)。 用户模型 创建一个包含保存用户信息、基于用户名和密码验证用户权限功能的 controller。 创建一个名为 User 的实体类,它是数据库中 USER 表的映射。需要的话,可以在其中添加其他属性。 还需要定义一个 UserRepository 类来保存用户信息,重写其 findByUsername 方法,在验证过程中会用到。 public interface UserRepository extends JpaRepository<User, String>{ User findByUsername(String username); } 千万不能在数据库中保存明文密码,因为很多用户喜欢在各种网站上使用相同的密码。 哈希算法有很多,BCrypt是最常用的之一,它也是推荐用于安全加密的算法。关于这个话题的更多内容,可以查看 这篇文章。 为了加密密码,我们在 @bean 注解标记的主类中定义一个 BCrypt Bean,如下所示: @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); } 加密密码的时候将会调用这个Bean里面的方法。 创建一个名为 UserController 的类,为其添加 @RestController 注解并定义路由映射。 在这个应用中,我们接收前端传入的 UserDto 对象来保存用户信息。你也可以选择在 @RequestBody 参数中接收 User 对象。 @RestController @RequestMapping("/api/services/controller/user") @AllArgsConstructor public class UserController { private UserService userService; @PostMapping() public ResponseEntity<String> saveUser(@RequestBody UserDto userDto) { return new ResponseEntity<>(userService.saveDto(userDto), HttpStatus.OK); } } 我们使用之前定义的 BCrypt Bean 来加密传入的 UserDto 对象的 password 字段。这个操作也可以在 controller 之中执行,但是把逻辑操作集中到 service 类中是更好的做法。 @Transactional(rollbackFor = Exception.class) public String saveDto(UserDto userDto) { userDto.setPassword(bCryptPasswordEncoder.encode(userDto.getPassword())); return save(new User(userDto)).getId(); } 验证过滤器 需要通过权限验证来确定用户的真实身份。这里我们使用经典的【用户名-密码对】的形式来完成。 验证步骤: 创建继承 UsernamePasswordAuthenticationFilter 的验证过滤器 创建继承 WebSecurityConfigurerAdapter 的安全配置类并应用过滤器 验证过滤器的代码如下——也许你已经知道了,过滤器是 Spring Security 的核心。 public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authenticationManager; public JWTAuthenticationFilter(AuthenticationManager authenticationManager) { this.authenticationManager = authenticationManager; setFilterProcessesUrl("/api/services/controller/user/login"); } @Override public Authentication attemptAuthentication(HttpServletRequest req, HttpServletResponse res) throws AuthenticationException { try { User creds = new ObjectMapper().readValue(req.getInputStream(), User.class); return authenticationManager.authenticate( new UsernamePasswordAuthenticationToken( creds.getUsername(), creds.getPassword(), new ArrayList<>()) ); } catch (IOException e) { throw new RuntimeException(e); } } @Override protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse res, FilterChain chain, Authentication auth) throws IOException { String token = JWT.create() .withSubject(((User) auth.getPrincipal()).getUsername()) .withExpiresAt(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .sign(Algorithm.HMAC512(SECRET.getBytes())); String body = ((User) auth.getPrincipal()).getUsername() + " " + token; res.getWriter().write(body); res.getWriter().flush(); } } Spring Security 默认使用继承了 UsernamePasswordAuthenticationFilter 的子类进行密码验证 ,我们可以在其中编写自定义的验证逻辑。 我们在构造函数中调用setFilterProcessesUrl 方法,设置默认登录地址。 如果删除这行代码,Spring Security 会生成一个默认的 “/login” 端点,我们可以不用在 controller 中显式地定义登录端点。 这行代码执行之后,我们的登录端点将被设置为 /api/services/controller/user/login,你可以根据自己的实际代码来设置。 我们重写了 UsernameAuthenticationFilter 类的 attemptAuthentication 和 successfulAuthentication 方法。 用户登录时会执行 attemptAuthentication方法,它会读取凭证信息、创建用户 POJO、校验凭证并授权。 我们传入用户名、密码以及一个空列表。我们还没有定义用户角色,所以把这个表示用户权限(角色)的列表留空就行。 如果验证成功,就会执行 successfulAuthentication 方法,它的参数由Spring Security自动注入。 attemptAuthentication返回Authentication对象,这个对象包含了我们传入的权限信息。 我们想在验证成功之后返回一个使用用户名、密钥和过期时间创建的 token。先定义SECRET和 EXPIRATION_DATE。 public class SecurityConstants { public static final String SECRET = "SECRET_KEY"; public static final long EXPIRATION_TIME = 900_000; // 15 mins public static final String TOKEN_PREFIX = "Bearer "; public static final String HEADER_STRING = "Authorization"; public static final String SIGN_UP_URL = "/api/services/controller/user"; } 创建一个类作为常量的容器,SECRET 的值可以任意设置,最佳的做法是在 hash 算法支持的范围内使用尽可能长的字符串。例如我们使用的是 HS256 算法,SECRET 字符串的最佳长度即为 256 bits/32 个字符。 超时时间设置为 15 分钟,这是防御暴力破解密码的最佳实践。此处使用的时间单位为毫秒。 验证过滤器准备好了,但还不可用,我们还要创建一个授权过滤器,再通过一个配置类来应用它们。 授权过滤器会校验 Authorization 请求头中的 token 是否存在及其可用性。在配置类中指明哪些端点需要使用这个过滤器。 授权过滤器 doFilterInternal 方法拦截请求并校验 Authorization 请求头,如果不存在或者它的值不是以 “BEARER” 开头,则直接转到下一个过滤器。 如果这个请求头携带了合法的值,会调用 getAuthentication 方法,校验这个 JWT,如果这个 token 是可用的,它会返回一个Spring内部使用的 token。 这个新生成的 token 会被保存在 SecurityContext 中,如果需要基于用户角色进行授权的话,可以向这个 token 传入用户权限。 过滤器都准备好了,现在要通过配置类把它们投入使用。 public class JWTAuthorizationFilter extends BasicAuthenticationFilter { public JWTAuthorizationFilter(AuthenticationManager authManager) { super(authManager); } @Override protected void doFilterInternal(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException { String header = req.getHeader(HEADER_STRING); if (header == null || !header.startsWith(TOKEN_PREFIX)) { chain.doFilter(req, res); return; } UsernamePasswordAuthenticationToken authentication = getAuthentication(req); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(req, res); } // Reads the JWT from the Authorization header, and then uses JWT to validate the token private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) { String token = request.getHeader(HEADER_STRING); if (token != null) { // parse the token. String user = JWT.require(Algorithm.HMAC512(SECRET.getBytes())) .build() .verify(token.replace(TOKEN_PREFIX, "")) .getSubject(); if (user != null) { // new arraylist means authorities return new UsernamePasswordAuthenticationToken(user, null, new ArrayList<>()); } return null; } return null; } 配置处理 给这个类添加 @EnableWebSecurity 注解,同时让它继承 WebSecurityConfigureAdapter 并实现自定义的安全逻辑。 自动注入之前定义的 BCrypt Bean,同时自动注入 UserDetailsService 用来获取用户账户信息。 最重要的是那个接收一个 HttpSecurity 对象作为参数的方法,其中声明了如何在各个端点中应用过滤器、配置了 CORS、放行了所有对注册接口的 POST 请求。 可以添加其他匹配器来基于 URL 模式和角色进行过滤,你也可以 查看 StackOverflow 上这个问题的相关示例。另一个方法配置了 AuthenticationManager 在登录校验时使用我们指定的编码器。 @EnableWebSecurity public class WebSecurity extends WebSecurityConfigurerAdapter { private UserDetailsServiceImpl userDetailsService; private BCryptPasswordEncoder bCryptPasswordEncoder; public WebSecurity(UserDetailsServiceImpl userService, BCryptPasswordEncoder bCryptPasswordEncoder) { this.userDetailsService = userService; this.bCryptPasswordEncoder = bCryptPasswordEncoder; } @Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().authorizeRequests() .antMatchers(HttpMethod.POST, SIGN_UP_URL).permitAll() .anyRequest().authenticated() .and() .addFilter(new JWTAuthenticationFilter(authenticationManager())) .addFilter(new JWTAuthorizationFilter(authenticationManager())) // this disables session creation on Spring Security .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder); } @Bean CorsConfigurationSource corsConfigurationSource() { final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration().applyPermitDefaultValues(); source.registerCorsConfiguration("/**", corsConfiguration); return source; } } 测试实现 发送一些请求来测试应用是否正常工作。 使用 GET 请求访问受保护的资源,服务端返回了 403 状态码。 这是程序设计预期的行为,因为我们没有在请求头中携带 token 信息。 现在创建一个用户: 发送一个携带了用户信息数据的 POST 请求,以创建用户。稍后将登陆这个账户来获取 token。 获取到 token 了,现在可以用这个 token 来访问受保护的资源。 在 Authorization 请求头中携带 token,就可以访问受保护的端点了。 总结 Spring 中实现 JWT 授权和密码认证的步骤,同时学习了如何安全地保存用户信息。 参考内容 How to Set Up Java Spring Boot JWT Authorization and

优秀的个人博客,低调大师

文件系统(01):基于SpringBoot框架,管理Excel和PDF文件类型

本文源码:GitHub·点这里 || GitEE·点这里 一、文档类型简介 1、Excel文档 Excel一款电子表格软件。直观的界面、出色的计算功能和图表工具,在系统开发中,经常用来把数据转存到Excel文件,或者Excel数据导入系统中,这就涉及数据转换问题。 2、PDF文档 PDF是可移植文档格式,是一种电子文件格式,具有许多其他电子文档格式无法相比的优点。PDF文件格式可以将文字、字型、格式、颜色及独立于设备和分辨率的图形图像等封装在一个文件中。该格式文件还可以包含超文本链接、声音和动态影像等电子信息,支持特长文件,集成度和安全可靠性都较高。 二、Excel文件管理 1、POI依赖 Apache POI是Apache软件基金会的开源类库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能。 <!-- Excel 依赖 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.9</version> </dependency> <!-- 2007及更高版本 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.9</version> </dependency> 2、文件读取 public static List<List<Object>> readExcel(String path) throws Exception { File file = new File(path) ; List<List<Object>> list = new LinkedList<>(); XSSFWorkbook xwb = new XSSFWorkbook(new FileInputStream(file)); // 读取 Sheet1 表格内容 XSSFSheet sheet = xwb.getSheetAt(0); // 读取行数:不读取Excel表头 for (int i = (sheet.getFirstRowNum()+1); i <= (sheet.getPhysicalNumberOfRows()-1); i++) { XSSFRow row = sheet.getRow(i); if (row == null) { continue; } List<Object> linked = new LinkedList<>(); for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) { XSSFCell cell = row.getCell(j); if (cell == null) { continue; } Object value ; // 这里需根据实际业务情况处理 switch (cell.getCellType()) { case XSSFCell.CELL_TYPE_NUMERIC: //处理数值带{.0}问题 value = Double.valueOf(String.valueOf(cell)).longValue() ; break; default: value = cell.toString(); } linked.add(value); } if (linked.size()!= 0) { list.add(linked); } } return list; } 3、文件创建 public static void createExcel(String excelName, String[] headList,List<List<Object>> dataList) throws Exception { // 创建 Excel 工作簿 XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(); // 创建表头 XSSFRow row = sheet.createRow(0); for (int i = 0; i < headList.length; i++) { XSSFCell cell = row.createCell(i); cell.setCellType(XSSFCell.CELL_TYPE_STRING); cell.setCellValue(headList[i]); } //添加数据 for (int line = 0; line < dataList.size(); line++) { XSSFRow rowData = sheet.createRow(line+1); List<Object> data = dataList.get(line); for (int j = 0; j < headList.length; j++) { XSSFCell cell = rowData.createCell(j); cell.setCellType(XSSFCell.CELL_TYPE_STRING); cell.setCellValue((data.get(j)).toString()); } } FileOutputStream fos = new FileOutputStream(excelName); workbook.write(fos); fos.flush(); fos.close(); } 4、文件导出 public static void exportExcel(String[] headList, List<List<Object>> dataList, OutputStream outputStream) throws Exception { // 创建 Excel 工作簿 XSSFWorkbook workbook = new XSSFWorkbook(); XSSFSheet sheet = workbook.createSheet(); // 创建表头 XSSFRow row = sheet.createRow(0); for (int i = 0; i < headList.length; i++) { XSSFCell cell = row.createCell(i); cell.setCellType(XSSFCell.CELL_TYPE_STRING); cell.setCellValue(headList[i]); } //添加数据 for (int line = 0; line < dataList.size(); line++) { XSSFRow rowData = sheet.createRow(line+1); List<Object> data = dataList.get(line); for (int j = 0; j < headList.length; j++) { XSSFCell cell = rowData.createCell(j); cell.setCellType(XSSFCell.CELL_TYPE_STRING); cell.setCellValue((data.get(j)).toString()); } } workbook.write(outputStream); outputStream.flush(); outputStream.close(); } 5、文件导出接口 @RestController public class ExcelWeb { @RequestMapping("/web/outExcel") public void outExcel (HttpServletResponse response) throws Exception { String exportName = "2020-01-user-data" ; response.setContentType("application/vnd.ms-excel"); response.addHeader("Content-Disposition", "attachment;filename="+ URLEncoder.encode(exportName, "UTF-8") + ".xlsx"); List<List<Object>> dataList = ExcelUtil.readExcel("F:\\file-type\\user-excel.xlsx") ; String[] headList = new String[]{"用户ID", "用户名", "手机号"} ; ExcelUtil.exportExcel(headList,dataList,response.getOutputStream()) ; } } 三、PDF文件管理 1、IText依赖 iText是一种生成PDF报表的Java组件。通过在服务器端使用页面或API封装生成PDF报表,客户端可以通过超链接直接显示或下载到本地,在系统开发中通常用来生成比较正式的报告或者合同类的电子文档。 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</artifactId> <version>5.5.11</version> </dependency> <dependency> <groupId>com.itextpdf.tool</groupId> <artifactId>xmlworker</artifactId> <version>5.5.11</version> </dependency> 2、API二次封装 首先对于Itext提供的API做一下表格、段落、图片等基础样式的二次封装,可以更好的适配业务。 public class PdfFontUtil { private PdfFontUtil(){} /** * 段落样式获取 */ public static Paragraph getParagraph (String content, Font font,Integer alignment){ Paragraph paragraph = new Paragraph(content,font) ; if (alignment != null && alignment >= 0){ paragraph.setAlignment(alignment); } return paragraph ; } /** * 图片样式 */ public static Image getImage (String imgPath,float width,float height) throws Exception { Image image = Image.getInstance(imgPath); image.setAlignment(Image.MIDDLE); if (width > 0 && height > 0){ image.scaleAbsolute(width, height); } return image ; } /** * 表格生成 */ public static PdfPTable getPdfPTable01 (int numColumns,float totalWidth) throws Exception { // 表格处理 PdfPTable table = new PdfPTable(numColumns); // 设置表格宽度比例为%100 table.setWidthPercentage(100); // 设置宽度:宽度平均 table.setTotalWidth(totalWidth); // 锁住宽度 table.setLockedWidth(true); // 设置表格上面空白宽度 table.setSpacingBefore(10f); // 设置表格下面空白宽度 table.setSpacingAfter(10f); // 设置表格默认为无边框 table.getDefaultCell().setBorder(0); table.setPaddingTop(50); table.setSplitLate(false); return table ; } /** * 表格内容 */ public static PdfPCell getPdfPCell (Phrase phrase){ return new PdfPCell (phrase) ; } /** * 表格内容带样式 */ public static void addTableCell (PdfPTable dataTable,Font font,List<String> cellList){ for (String content:cellList) { dataTable.addCell(getParagraph(content,font,-1)); } } } 3、生成PDF文件 这里基于上面的工具类,画一个PDF页面作为参考。 public class PdfPage01 { // 基础配置 private static String PDF_SITE = "F:\\file-type\\PDF页面2020-01-15.pdf" ; private static String FONT = "C:/Windows/Fonts/simhei.ttf"; private static String PAGE_TITLE = "PDF数据导出报告" ; // 基础样式 private static Font TITLE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H,20, Font.BOLD); private static Font NODE_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H,15, Font.BOLD); private static Font BLOCK_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H,13, Font.BOLD, BaseColor.BLACK); private static Font INFO_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H,12, Font.NORMAL,BaseColor.BLACK); private static Font CONTENT_FONT = FontFactory.getFont(FONT, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED); private static void createPdfPage () throws Exception { // 创建文档 Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(PDF_SITE)); document.open(); // 报告标题 document.add(PdfFontUtil.getParagraph(PAGE_TITLE,TITLE_FONT,1)) ; document.add(PdfFontUtil.getParagraph("\n商户名称:XXX科技有限公司",INFO_FONT,-1)) ; document.add(PdfFontUtil.getParagraph("\n生成时间:2020-01-15\n\n",INFO_FONT,-1)) ; // 报告内容 // 段落标题 + 报表图 document.add(PdfFontUtil.getParagraph("城市数据分布统计",NODE_FONT,-1)) ; document.add(PdfFontUtil.getParagraph("\n· 可视化图表\n\n",BLOCK_FONT,-1)) ; // 设置图片宽高 float documentWidth = document.getPageSize().getWidth() - document.leftMargin() - document.rightMargin(); float documentHeight = documentWidth / 580 * 320; document.add(PdfFontUtil.getImage("F:\\file-type\\myChart.jpg",documentWidth-80,documentHeight-80)) ; // 数据表格 document.add(PdfFontUtil.getParagraph("\n· 数据详情\n\n",BLOCK_FONT,-1)) ; PdfPTable dataTable = PdfFontUtil.getPdfPTable01(4,400) ; // 设置表格 List<String> tableHeadList = tableHead () ; List<List<String>> tableDataList = getTableData () ; PdfFontUtil.addTableCell(dataTable,CONTENT_FONT,tableHeadList); for (List<String> tableData : tableDataList) { PdfFontUtil.addTableCell(dataTable,CONTENT_FONT,tableData); } document.add(dataTable); document.add(PdfFontUtil.getParagraph("\n· 报表描述\n\n",BLOCK_FONT,-1)) ; document.add(PdfFontUtil.getParagraph("数据报告可以监控每天的推广情况," + "可以针对不同的数据表现进行分析,以提升推广效果。",CONTENT_FONT,-1)) ; document.newPage() ; document.close(); writer.close(); } private static List<List<String>> getTableData (){ List<List<String>> tableDataList = new ArrayList<>() ; for (int i = 0 ; i < 3 ; i++){ List<String> tableData = new ArrayList<>() ; tableData.add("浙江"+i) ; tableData.add("杭州"+i) ; tableData.add("276"+i) ; tableData.add("33.3%") ; tableDataList.add(tableData) ; } return tableDataList ; } private static List<String> tableHead (){ List<String> tableHeadList = new ArrayList<>() ; tableHeadList.add("省份") ; tableHeadList.add("城市") ; tableHeadList.add("数量") ; tableHeadList.add("百分比") ; return tableHeadList ; } public static void main(String[] args) throws Exception { createPdfPage () ; } } 4、页面效果 四、网页转PDF 1、页面Jar包依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> 2、编写页面样式 <!DOCTYPE html> <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml"> <head> <meta charset="UTF-8"/> <title>Title</title> <style> body{font-family:SimSun;} </style> </head> <body> 项目信息:<br/> 名称:${name}<br/> 作者:${author}<br/><br/> <img src="https://img2018.cnblogs.com/blog/1691717/201906/1691717-20190603213911854-1098366582.jpg"/> <br/> </body> </html> 3、核心配置类 public class PageConfig { private static final String DEST = "F:\\file-type\\HTML页面2020-01-15.pdf"; private static final String HTML = "/pdf_page_one.html"; private static final String FONT = "C:/Windows/Fonts/simsun.ttc"; private static Configuration freemarkerCfg = null ; static { freemarkerCfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS); //freemarker的模板目录 try { String path = "TODO:模板路径{自定义}" ; freemarkerCfg.setDirectoryForTemplateLoading(new File(path)); } catch (IOException e) { e.printStackTrace(); } } /** * 创建文档 */ private static void createPdf(String content,String dest) throws Exception { Document document = new Document(); PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(dest)); document.open(); XMLWorkerFontProvider fontImp = new XMLWorkerFontProvider(XMLWorkerFontProvider.DONTLOOKFORFONTS); fontImp.register(FONT); XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(content.getBytes()), null, Charset.forName("UTF-8"), fontImp); document.close(); } /** * 页面渲染 */ private static String freeMarkerRender(Map<String, Object> data, String htmlTmp) throws Exception { Writer out = new StringWriter(); Template template = freemarkerCfg.getTemplate(htmlTmp,"UTF-8"); template.process(data, out); out.flush(); out.close(); return out.toString(); } /** * 方法入口 */ public static void main(String[] args) throws Exception { Map<String,Object> data = new HashMap<> (); data.put("name","smile"); data.put("author","知了") ; String content = PageConfig.freeMarkerRender(data,HTML); PageConfig.createPdf(content,DEST); } } 4、转换效果图 五、源代码地址 文中涉及文件类型,在该章节源码ware18-file-parent/case-file-type目录下。 GitHub·地址 https://github.com/cicadasmile/middle-ware-parent GitEE·地址 https://gitee.com/cicadasmile/middle-ware-parent 推荐往期阅读: 微服务架构专题 《1、项目技术选型简介,架构图解说明》 《2、业务架构设计,系统分层管理》 《3、数据库选型简介,业务数据规划设计》 《4、中间件集成,公共服务封装》 《5、SpringCloud 基础组件应用设计》 《6、通过业务、应用、技术、存储方面,聊聊架构》

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册