1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > 根据文件模板实现预览 生成word pdf excel(后端-项目)

根据文件模板实现预览 生成word pdf excel(后端-项目)

时间:2019-07-09 05:05:26

相关推荐

根据文件模板实现预览 生成word pdf excel(后端-项目)

背景

页面上有一份表单,包含基本信息和列表信息,用户填写完信息后,点击预览/导出,可以预览/导出word、pdf、excel文档。

因为代码重构过,所以看流程可能会有点绕,为方便能看懂,这里直接提供了项目的地址

文件生成项目

期望你在本文能收获什么

一套比较完整的文件生成的解决方案和思路

一些代码构建的思路和细节,文中代码的一些组成是我参考在用项目的结构,重构改过来的,用上了一些设计模式,扩展性、规范性应该也还是可以的。

效果示例

doc文件模板

doc生成效果

pdf效果一样

excel文件模板

excel生成效果

文件word、pdf、excel生成/预览的基本流程

前端页面,用户填写用户信息,可以包含基本信息和列表信息,用户提交数据后端接收数据后依靠第三方的组件/工具,把文件生成到本地把生成到本地的文件上传到文件服务器并返回文件id/文件访问地址删除本地生成的文件把文件id/文件访问地址返回给前端
有的项目组做项目喜欢上传/生成文件再上传后在数据库存文件信息,然后返回文件地址给前端,前端拿到文件地址就直接处理有的项目组做项目喜欢上传/生成文件再上传后在数据库存文件信息,然后返回文件id给前端,前端默认是要知道文件服务器的基础访问地址,所以让前端拿到文件id后他们就会处理了(拼接字符串为文件访问地址(文件id为生成文件名)如 “http://文件地址/文件id”,或者再发个请求到数据库查文件具体信息(拿到地址)都可以)本文使用的是第二种。单独有文件服务器做文件上传,然后把上传成功的文件的基本信息保存到数据库,然后返回文件id给前端。本文主要是介绍如何生成各种类型的文档,所以不包含如何上传文件部分。当然市面上常用的文件上传服务实现这么多,相信你也是可以找到合适的解决方案的。如果以后有空,再给补一个通用的文件上传服务好了,哈哈

主要用到的工具

FreeMarker(生成word)、aspose(将word转换成pdf)、easyExcel(生成excel)

代码实现

基础工具类及依赖

maven项目spring boot 项目基础依赖

maven依赖

<?xml version="1.0" encoding="UTF-8"?><project xmlns="/POM/4.0.0"xmlns:xsi="/2001/XMLSchema-instance"xsi:schemaLocation="/POM/4.0.0 /xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.wwt.file</groupId><artifactId>FileHandle</artifactId><version>1.0-SNAPSHOT</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.5.RELEASE</version></parent><properties><java.version>1.8</java.version></properties><dependencies><!--mysql运行时依赖--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--测试的起步依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--lombok用来简化实体类--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--用来生成word文档--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency><!--hutool--><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.5.2</version></dependency><!-- 转化pdf start --><dependency><groupId>aspose-words</groupId><artifactId>aspose-words</artifactId><version>15.8.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/resources/lib/aspose-words-15.8.0-jdk16.jar</systemPath></dependency><!--转excel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency><!--这个注意要加,我在看官网的时候没看到,只加了上面那个,结果后来实测就少了东西报错了--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel-core</artifactId><version>3.1.1</version></dependency></dependencies></project>

application.yml

#mysql数据库连接spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/xxx?serverTimezone=GMT%2B8username: rootpassword: 123456

这里看到我用了mysql,其实我这里数据是由前端传过来的,不是查出来的,所以没用到数据库。只是加个配置项目启动就不会找默认配置报错。当然你可以的通过加个注解的方式解决,这里不提了。

项目结构

4. 基本类

我把包名也复制进去,希望尽量能让你还原项目

实体类

User - 数据实体类

package com.wwt.file.entity;import lombok.Data;@Datapublic class User {private Long id;private String name;private Integer age;private String email;}

FileVO - 文件返回信息

package com.wwt.file.entity.vo;import lombok.Data;/*** 封装文件返回信息**/@Datapublic class FileVO {/*** 文件名*/private String fileName;/*** 文件id*/private Long fileId;public FileVO(String fileName, Long fileId) {this.fileName = fileName;this.fileId = fileId;}}

UserExportDTO

package com.wwt.file.entity.dto;import cn.hutool.core.map.MapUtil;import com.wwt.file.entity.User;import lombok.Data;import java.util.HashMap;import java.util.List;import java.util.Map;/*** 封装前端传过来的用户请求信息*/@Datapublic class UserExportDTO {private User user1;private User user2;private List<User> users;/**** @return*/public Map<String, Object> structDataMap() {Map<String, Object> map = MapUtil.createMap(HashMap.class);map.put("user1",this.user1);map.put("user2",this.user2);map.put("users",this.users);return map;}}

在controller中的使用示例

枚举类

FileTypeEnum - 文件类型的枚举类

package com.wwt.file.enums;/*** 文件类型*/public enum FileTypeEnum {WORD("word",".doc"),PDF("pdf",".pdf"),EXCEL("excel",".xlsx"),XML("xml",".xml");/*** 类型*/private String type;/*** 后缀*/private String suffix;FileTypeEnum(String type, String suffix) {this.type = type;this.suffix = suffix;}public String getType() {return type;}public String getSuffix() {return suffix;}}

UserTemplate - 用户模板

package com.wwt.file.enums;/*** 用户模板*/public enum UserTemplate {USER_TEMPLATE_XML("user_template","用户信息","这是一个用户模板",FileTypeEnum.XML),USER_TEMPLATE_EXCEL("user_template","用户信息","这是一个用户模板",FileTypeEnum.EXCEL);/*** 模板名称*/private String template;/*** 模板中文名*/private String chName;/*** 详情*/private String des;/*** 模板类型*/private String type;/*** 模板后缀*/private String suffix;UserTemplate(String template, String chName, String des, FileTypeEnum fileTypeEnum) {this.chName = chName;this.des = des;this.template = template + fileTypeEnum.getSuffix();this.type = fileTypeEnum.getType();this.suffix = fileTypeEnum.getSuffix();}public String getTemplate() {return template;}public String getChName() {return chName;}public String getDes() {return des;}public String getType() {return type;}public String getSuffix() {return suffix;}}

这两个枚举类的关联关系

FileTypeEnum :该枚举是单一职责的,只表示文件的类型信息

UserTemplate :通过构造方法引入 FileTypeEnum ,在构造方法中接入 fileTypeEnum 实现

UserTemplate 基本信息的初始化,创建 UserTemplate 时更加清晰明了。

不太好的实现是这样的:

USER_TEMPLATE_XML("user_template","用户信息","这是一个用户模板",FileTypeEnum.XML.getSuffix(),FileTypeEnum.XML.getType()),USER_TEMPLATE_EXCEL("user_template","用户信息","这是一个用户模板",FileTypeEnum.EXCEL.getSuffix(),FileTypeEnum.EXCEL.getType());UserTemplate(String template, String chName, String des, String suffix,String type) {this.chName = chName;this.des = des;this.template = template + suffix;this.type = type;this.suffix = suffix;}//又或者不创建 FileTypeEnum ,直接在这些显示文件类型信息的地方都用字符串写死

统一响应结果

Result - 一个抽象的返回结果类

package com.wwt.file.result;import com.wwt.file.result.enums.ResultCodeEnum;import lombok.Data;@Datapublic abstract class Result<T> {public static Result success(){Result r = new Result(){};r.setSuccess(ResultCodeEnum.SUCCESS.getSuccess());r.setCode(ResultCodeEnum.SUCCESS.getCode());r.setMessage(ResultCodeEnum.SUCCESS.getMessage());return r;}public static Result error(){Result r = new Result(){};r.setSuccess(ResultCodeEnum.UNKNOWN_REASON.getSuccess());r.setCode(ResultCodeEnum.UNKNOWN_REASON.getCode());r.setMessage(ResultCodeEnum.UNKNOWN_REASON.getMessage());return r;}/*** 是否成功*/private Boolean success;/*** 响应码*/private Integer code;/*** 响应信息*/private String message;/*** 响应数据*/private T data;public Result data(T data){this.setData(data);return this;}public Result(){}}

DefaultResult - Result的实现,默认的响应结果类,平时用这个类做响应结果就可以了

package com.wwt.file.result;import com.wwt.file.result.enums.ResultCodeEnum;/*** 默认响应结果* @param <T>*/public class DefaultResult<T> extends Result<T> {public static DefaultResult setResult(ResultCodeEnum resultCodeEnum){DefaultResult r = new DefaultResult();r.setSuccess(resultCodeEnum.getSuccess());r.setCode(resultCodeEnum.getCode());r.setMessage(resultCodeEnum.getMessage());return r;}}

ResultCodeEnum - 常用响应结果,有什么想用的响应结果,在这里增加枚举结果对象即可

package com.wwt.file.result.enums;import lombok.Getter;import lombok.ToString;//这个类是在尚硅谷的相关资料里面抄过来的@Getter@ToStringpublic enum ResultCodeEnum {SUCCESS(true, 20000,"成功"),UNKNOWN_REASON(false, 20001, "未知错误"),BAD_SQL_GRAMMAR(false, 21001, "sql语法错误"),JSON_PARSE_ERROR(false, 21002, "json解析异常"),PARAM_ERROR(false, 21003, "参数不正确"),FILE_UPLOAD_ERROR(false, 21004, "文件上传错误"),FILE_DELETE_ERROR(false, 21005, "文件刪除错误"),EXCEL_DATA_IMPORT_ERROR(false, 21006, "Excel数据导入错误"),VIDEO_UPLOAD_ALIYUN_ERROR(false, 22001, "视频上传至阿里云失败"),VIDEO_UPLOAD_TOMCAT_ERROR(false, 22002, "视频上传至业务服务器失败"),VIDEO_DELETE_ALIYUN_ERROR(false, 2, "阿里云视频文件删除失败"),FETCH_VIDEO_UPLOADAUTH_ERROR(false, 2, "获取上传地址和凭证失败"),REFRESH_VIDEO_UPLOADAUTH_ERROR(false, 2, "刷新上传地址和凭证失败"),FETCH_PLAYAUTH_ERROR(false, 2, "获取播放凭证失败"),URL_ENCODE_ERROR(false, 23001, "URL编码失败"),ILLEGAL_CALLBACK_REQUEST_ERROR(false, 23002, "非法回调请求"),FETCH_ACCESSTOKEN_FAILD(false, 23003, "获取accessToken失败"),FETCH_USERINFO_ERROR(false, 23004, "获取用户信息失败"),LOGIN_ERROR(false, 23005, "登录失败"),COMMENT_EMPTY(false, 24006, "评论内容必须填写"),PAY_RUN(false, 25000, "支付中"),PAY_UNIFIEDORDER_ERROR(false, 25001, "统一下单错误"),PAY_ORDERQUERY_ERROR(false, 25002, "查询支付结果错误"),ORDER_EXIST_ERROR(false, 25003, "课程已购买"),GATEWAY_ERROR(false, 26000, "服务不能访问"),CODE_ERROR(false, 28000, "验证码错误"),LOGIN_PHONE_ERROR(false, 28009, "手机号码不正确"),LOGIN_MOBILE_ERROR(false, 28001, "账号不正确"),LOGIN_PASSWORD_ERROR(false, 28008, "密码不正确"),LOGIN_DISABLED_ERROR(false, 28002, "该用户已被禁用"),REGISTER_MOBLE_ERROR(false, 28003, "手机号已被注册"),LOGIN_AUTH(false, 28004, "需要登录"),LOGIN_ACL(false, 28005, "没有权限"),SMS_SEND_ERROR(false, 28006, "短信发送失败"),SMS_SEND_ERROR_BUSINESS_LIMIT_CONTROL(false, 28007, "短信发送过于频繁");private Boolean success;private Integer code;private String message;ResultCodeEnum(Boolean success, Integer code, String message) {this.success = success;this.code = code;this.message = message;}}

正因为感觉把所有的响应结果都封装在一个类里面,后期类就太长了,所以我才把响应结果类 Result 做抽象化,让其实现类的 setResult() 方法来实现不同的响应结果类的入参。当然怕麻烦的话,就直接用 DefaultResult 和 ResultCodeEnum 基本就够用了。

我们这里用到的,扩展其他的响应结果枚举类,以及返回结果类的示例如下:

FileResult - 文件结果类

package com.wwt.file.result.file;import com.wwt.file.result.Result;import com.wwt.file.result.enums.FileResultEnum;/*** 文件响应结果* @param <T>*/public class FileResult<T> extends Result<T> {public static FileResult setResult(FileResultEnum FileResultEnum){FileResult r = new FileResult();r.setSuccess(FileResultEnum.getSuccess());r.setCode(FileResultEnum.getCode());r.setMessage(FileResultEnum.getMessage());return r;}}

FileResultEnum - 文件返回结果枚举对象 - 从 ResultCodeEnum 中分离出来了

package com.wwt.file.result.enums;import lombok.Getter;import lombok.ToString;@Getter@ToStringpublic enum FileResultEnum {//只要不是20000都是失败SUCCESS(true, 20000,"成功"),UNKNOWN_REASON(false, 20001, "未知错误"),UPLOAD_SUCCESS(true, 20000, "文件上传成功"),UPLOAD_FAIL(false, 20002, "文件上传失败");private Boolean success;private Integer code;private String message;FileResultEnum(Boolean success, Integer code, String message) {this.success = success;this.code = code;this.message = message;}}

只要新的响应结果类和新的响应结果枚举类能用 setResult()方法使其一对一关联起来即可

新的响应结果类继承 Result 类即可

使用示例

工具类

FileUtil

package com.wwt.file.util;import cn.hutool.core.io.resource.ClassPathResource;import cn.hutool.core.lang.UUID;import cn.hutool.core.util.ObjectUtil;import cn.hutool.core.util.StrUtil;import com.wwt.file.enums.FileTypeEnum;/*** 文件处理工具类*/public class FileUtil {/*** 获取项目根路径* pathStr : D:\code\javaCode\FileHandle\target\classes* 如果是在ide中运行,则和target同级目录,如果是jar部署到服务器,则默认和jar包同级* pathStr : D:\code\javaCode\FileHandle\ FileHandle是我的项目名*/public static String getResourceBasePath() {ClassPathResource classPathResource = new ClassPathResource("");String pathStr = classPathResource.getFile().getAbsolutePath();pathStr = pathStr.replace("target\\classes", "");return pathStr;}/*** 如果父目录不存在,则创建* 保证父目录一定存在* 否则文件可能创建失败* 支持创建多级目录* @Param filePath 文件绝对路径*/public static void mkParentDirIfNotExist(String filePath) {cn.hutool.core.io.FileUtil.mkParentDirs(filePath);}/*** 删除文件或目录及其下文件* @param filePath 文件绝对路径*/public static void delDir(String filePath){cn.hutool.core.io.FileUtil.del(filePath);}/*** 生成随机文件名* @param fileTypeEnum 指定文件类型* @return*/public static String generateFileName(FileTypeEnum fileTypeEnum){if (ObjectUtil.isNull(fileTypeEnum)){return null;}//System.currentTimeMillis() 如果你不想用uuid,用时间也是可以的return StrUtil.concat(true, UUID.randomUUID().toString(true), fileTypeEnum.getSuffix());}}

生成word文档

原理

word文档生成主要用到的工具是freemarker提供一份word文档模板,在需要插入数据的地方填写占位符,代码中将数据和文档绑定时,会用实际数据替换掉占位符

步骤

依赖

<!--用来生成word文档--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency>

新建项目模板目录

新建项目模板

另存为xml文件

把文件模板放进template目录

用notepad++之类的工具类打开并修改模板文件

实体类数据

小提示:如果文件内容不是格式化的,你复制内容到idea里的随便一个xml文档,ctrl+alt+L代码格式化之后再复制回来也是可以的。

为什么先预写数据,然后再查找替换。而不是在word文档里面直接写${xxx.xxx},然后生成xml文件呢?因为实测过可能$符号会因为一些格式问题,预写替换符再成xml的话会出奇怪的问题,比如说$符号和属性分开了,导致数据填不进去,或者报错。

list数据

全部替换后,转为word文档打开应该是如下面这个样子的。但是因为加了list标签的原因,可能替换完就打不开了。不过还是再看看替换完应该是长什么样的吧。

前端数据

{"user1": {"id": 1, "name": "张三", "age": 77, "email": "22667344.@"}, "user2": {"id": 19, "name": "李四", "age": 68, "email": "33447899@"}, "users": [{"id": 33, "name": "帅哥1", "age": 18, "email": "23217744@"}, {"id": 34, "name": "帅哥2", "age": 18, "email": "23217744@"}, {"id": 35, "name": "帅哥3", "age": 18, "email": "23217744@"}, {"id": 36, "name": "帅哥4", "age": 18, "email": "23217744@"}, {"id": 37, "name": "帅哥5", "age": 18, "email": "23217744@"}, {"id": 38, "name": "帅哥6", "age": 18, "email": "23217744@"}, {"id": 39, "name": "帅哥7", "age": 18, "email": "23217744@"}, {"id": 40, "name": "帅哥8", "age": 18, "email": "23217744@"}]}

后端接收数据

controller写请求接口

package com.wwt.file.controller;import com.wwt.file.entity.User;import com.wwt.file.entity.dto.UserExportDTO;import com.wwt.file.entity.vo.FileVO;import com.wwt.file.result.DefaultResult;import com.wwt.file.result.Result;import com.wwt.file.result.enums.FileResultEnum;import com.wwt.file.result.file.FileResult;import com.wwt.file.service.IUserService;import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;import java.io.IOException;@RestController@RequestMapping("/user")public class UserController {@Resourceprivate IUserService userService;@PostMapping("/export")public Result<FileVO> export(@RequestBody UserExportDTO upd, String type){return FileResult.setResult(FileResultEnum.UPLOAD_SUCCESS).data(userService.export(upd,type));}}

service

IUserService接口

package com.wwt.file.service;import com.wwt.file.entity.dto.UserExportDTO;import com.wwt.file.entity.vo.FileVO;public interface IUserService {public FileVO export(UserExportDTO upd, String type);}

UserService实现类

package com.wwt.file.service.impl;import cn.hutool.core.util.StrUtil;import com.wwt.file.entity.dto.UserExportDTO;import com.wwt.file.entity.vo.FileVO;import com.wwt.file.enums.FileTypeEnum;import com.wwt.file.enums.UserTemplate;import com.wwt.file.service.ExportService;import com.wwt.file.service.IUserService;import com.wwt.file.util.excel.UserExcelDataWriter;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserService implements IUserService {@Resourcepublic ExportService exportService;@Overridepublic FileVO export(UserExportDTO upd, String type){//用工具类生成word文件if (StrUtil.equals(FileTypeEnum.WORD.getType(),type) || StrUtil.equals(FileTypeEnum.PDF.getType(),type)){return exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_XML.getTemplate(),null);}//生成excel的话要提供excelwriterreturn exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_EXCEL.getTemplate(),new UserExcelDataWriter());}}

解析

ExportService

package com.wwt.file.service;import cn.hutool.core.util.StrUtil;import com.wwt.file.entity.vo.FileVO;import com.wwt.file.enums.FileTypeEnum;import com.wwt.file.enums.UserTemplate;import com.wwt.file.util.DocUtil;import com.wwt.file.util.excel.ExcelDataWriter;import com.wwt.file.util.FileUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.io.File;import java.util.Map;@Slf4j@Servicepublic class ExportService {/*** 临时生成文件的路径*/private static final String TEMP_FILE_DIR = "tmp\\";public FileVO export(Map<String, Object> dataMap, String type ,String tempFileName,ExcelDataWriter writer){if (StrUtil.equals(FileTypeEnum.WORD.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_XML.getChName(),exportWord(dataMap,tempFileName));}if (StrUtil.equals(FileTypeEnum.PDF.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_XML.getChName(),exportPdf(dataMap,tempFileName));}if (StrUtil.equals(FileTypeEnum.EXCEL.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_EXCEL.getChName(),exportExcel(dataMap,writer));}return new FileVO(null,null);}/*** @param dataMap 填充的数据,每个值都不能为null,可以为empty* @param tempFileName 模板文件名,放置在template目录下* @return 文件服务器返回的fileId* @description 模板导出word**/public Long exportWord(Map<String, Object> dataMap, String tempFileName) {//生成文件名 - 随机文件名,防止重名String fileName = FileUtil.generateFileName(FileTypeEnum.WORD);DocUtil.saveWord(tempFileName, fileName, dataMap);//获取文件绝对路径String filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,fileName);//上传文件到文件服务器//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//删除生成的文件//FileUtil.delDir(filePath);//模拟上传成功返回的id return 1234L; //fileId}/*** @param dataMap 填充的数据,每个值都不能为null,可以为empty* @param tempFileName 模板文件名,放置在template目录下* @return 文件服务器返回的fileId* @description 模板导出pdf**/public Long exportPdf(Map<String, Object> dataMap, String tempFileName) {//生成文件名String fileName = FileUtil.generateFileName(FileTypeEnum.PDF);DocUtil.savePdf(tempFileName, fileName, dataMap);String filePath = new File(FileUtil.getResourceBasePath(), TEMP_FILE_DIR + fileName).getAbsolutePath();//上传文件到文件服务器//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//FileUtil.delDir(filePath);return 1234L; //fileId}/*** 导出excel* 包括生成文档和上传文件* @param dataMap 数据* @param writer 不同的writer实现类处理不同的模板写入* @return*/public Long exportExcel(Map<String, Object> dataMap,ExcelDataWriter writer){writer.export(dataMap);return 1234L;}}

DocUtil 为实现具体文件生成到本地的操作。因为 ExportService 中还涉及到文件上传的操作,所以又把这个生成操作单独提取了出来,封装到DocUtil 中,实现上传和生成的逻辑解耦。

DocUtil

主要看这个 saveWord 方法

package com.wwt.file.util;import cn.hutool.core.io.IoUtil;import cn.hutool.core.util.StrUtil;import cn.hutool.system.OsInfo;import cn.hutool.system.SystemUtil;import com.aspose.words.Document;import com.aspose.words.FontSettings;import com.aspose.words.License;import com.aspose.words.SaveFormat;import com.wwt.file.enums.FileTypeEnum;import freemarker.template.Template;import lombok.extern.slf4j.Slf4j;import java.io.*;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.util.Map;@Slf4jpublic class DocUtil {/*** 临时生成文件的路径*/private static final String TEMP_FILE_DIR = "tmp\\";private static final String LICENSE_FILENAME = "aspose_license.xml";/*** @param templateFileName 模板文件名称* @param fileName生成的文件名.doc结尾* @param dataMap数据,* @description 用模板生成word, 模板存放路径在resources/template下,生成的文件路径在jar包的同级目录/tmp路径下* @attention dataMap中的数据不能为null, 可用空值代替* @other 导出的doc文件其实是文本文件, 而docx文件是二进制文件(其实是一个压缩包).关于如何导出docx可参考 /wantLight/article/details/106105416**/public static void saveWord(String templateFileName, String fileName, Map<String, Object> dataMap) {Writer out = null;try {//根据配置获取FreeMarker模板对象Template template = FreeMarkerTemplateFactory.getTemplate(templateFileName);//拼接出即将生成文件的绝对路径 /xxx/xxx/xxx.docString filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,fileName);//如果文件父目录不存在,则创建父目录FileUtil.mkParentDirIfNotExist(filePath);// 创建一个Word文档的输出流out = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(new File(filePath).toPath()), StandardCharsets.UTF_8));template.process(dataMap, out);out.flush();} catch (Exception e) {log.error("saveWord error:{}", e.getMessage(), e);} finally {IoUtil.close(out);}}/*** @param templateFileName 模板文件名称* @param fileName 生成的文件名.pdf结尾* @param dataMap数据,* @description 用模板先生成生成word, 再转成pdf,模板存放路径在resources/template下,生成的文件路径在jar包的同级目录/tmp路径下* @attention dataMap中的数据不能为null, 可用空值代替**/public static void savePdf(String templateFileName, String fileName, Map<String, Object> dataMap) {InputStream docInputStream = null;OutputStream outputStream = null;try {String docFileName = fileName.replace(FileTypeEnum.PDF.getSuffix(), FileTypeEnum.WORD.getSuffix());//先生成wordsaveWord(templateFileName, docFileName, dataMap);String docFilePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,docFileName);//aspose将word转成pdfif (!getLicense()) {// 验证License 若不验证则转化出的pdf文档会有水印产生return;}OsInfo osInfo = SystemUtil.getOsInfo();//linux环境的字体,需要将c:\windows\fonts下的字体文件放到指定目录下,否则会乱码if (osInfo.isLinux()) {FontSettings.setFontsFolder("/usr/share/fonts/chinese", true);}docInputStream = Files.newInputStream(new File(docFilePath).toPath());File outputFile = new File(FileUtil.getResourceBasePath(), "tmp/" + fileName);outputStream = Files.newOutputStream(outputFile.toPath());Document doc = new Document(docInputStream);//sourcerFile是将要被转化的word文档doc.save(outputStream, SaveFormat.PDF);//全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换//删除生成的临时word文件FileUtil.delDir(docFilePath);} catch (Exception e) {log.error("savePdf error:{}", e.getMessage(), e);} finally {IoUtil.close(docInputStream);IoUtil.close(outputStream);}}/*** 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印** @return*/public static boolean getLicense() {boolean result = false;try {InputStream is = DocUtil.class.getClassLoader().getResourceAsStream(LICENSE_FILENAME);License asposeLic = new License();asposeLic.setLicense(is);result = true;} catch (Exception e) {log.error("getLicense error:{}", e.getMessage());}return result;}}

如果在测试word,还没用到pdf的功能,代码报错,先把报错的注释掉等会再测试就好了

FreeMarkerTemplateFactory - 用来创建 template对象

package com.wwt.file.util;import freemarker.template.Configuration;import freemarker.template.Template;import freemarker.template.Version;import java.io.IOException;/*** FreeMarker 创建模板对象的工厂*/public class FreeMarkerTemplateFactory {/*** FreeMarker的版本*/private static final String FREEMARKER_VERSION = "2.3.28";/*** 模板存放目录*/private static final String TEMPLATE_DIR = "/template";/*** 编码格式*/private static final String CHARSET = "utf-8";/*** 获取模板对象* @param templateFileName* @return* @throws IOException*/public static Template getTemplate(String templateFileName) throws IOException {Configuration configuration = new Configuration(new Version(FREEMARKER_VERSION));configuration.setDefaultEncoding("utf-8");//设置模板位置,默认是classpath下的指定目录下,idea中运行时为resources下的指定目录下configuration.setClassForTemplateLoading(DocUtil.class, TEMPLATE_DIR);Template template = configuration.getTemplate(templateFileName, CHARSET);return template;}}

测试

请求及数据

请求地址:http://localhost:8080/user/export?type=word

请求数据:

{"user1": {"id": 1, "name": "张三", "age": 77, "email": "22667344.@"}, "user2": {"id": 19, "name": "李四", "age": 68, "email": "33447899@"}, "users": [{"id": 33, "name": "帅哥1", "age": 18, "email": "23217744@"}, {"id": 34, "name": "帅哥2", "age": 18, "email": "23217744@"}, {"id": 35, "name": "帅哥3", "age": 18, "email": "23217744@"}, {"id": 36, "name": "帅哥4", "age": 18, "email": "23217744@"}, {"id": 37, "name": "帅哥5", "age": 18, "email": "23217744@"}, {"id": 38, "name": "帅哥6", "age": 18, "email": "23217744@"}, {"id": 39, "name": "帅哥7", "age": 18, "email": "23217744@"}, {"id": 40, "name": "帅哥8", "age": 18, "email": "23217744@"}]}

可能会遇到的问题

文件生成到本地不成功,没权限(window下)

Could not open/create prefs root node Software\JavaSoft\Prefs at root

参考:/yxlzfx/article/details/117767879数据填不进去word文档,但是代码执行没报错。这个一方面你要看看你模板中替换符的名字是否和你构建的map的key对应得上。另一方面,你是不是用过idea在启动项目的情况下修改了xml的模板文件,如果是,你看看target/class/template是不是没有替换符。我改用nodpad来修改xml就是这个意思,可能会修改不成功。打包打不进去修改了的内容。所以还是停止项目,用nodpad等工具来改比较好。

生成PDF文档

原理

生成pdf主要用的是 aspose流程就是先生成word文档到临时文件目录,然后用 aspose-word 将word文档转换成pdf,再上传到文件服务器返回文件id,再删除pdf文件,返回文件id给前端。生成word的工作调用上面已实现接口,即可。

步骤

下载jar包

地址: /repo/com/aspose/

进去后选一个版本下即可

把jar包添加到项目中

添加依赖

<!-- 转化pdf start --><dependency><groupId>aspose-words</groupId><artifactId>aspose-words</artifactId><version>15.8.0</version><scope>system</scope><systemPath>${project.basedir}/src/main/resources/lib/aspose-words-15.8.0-jdk16.jar</systemPath></dependency>

注意版本和jar包名字要对应的上

添加认证文件

<License><Data><Products><Product>Aspose.Total for Java</Product><Product>Aspose.Words for Java</Product></Products><EditionType>Enterprise</EditionType><SubscriptionExpiry>20991231</SubscriptionExpiry><LicenseExpiry>20991231</LicenseExpiry><SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7</SerialNumber></Data><Signature>sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=</Signature></License>

Service

UserService

没有改变,因为都是通过 ExportService 的export 接口根据类型判断统一实现的。

package com.wwt.file.service.impl;import cn.hutool.core.util.StrUtil;import com.wwt.file.entity.dto.UserExportDTO;import com.wwt.file.entity.vo.FileVO;import com.wwt.file.enums.FileTypeEnum;import com.wwt.file.enums.UserTemplate;import com.wwt.file.service.ExportService;import com.wwt.file.service.IUserService;import com.wwt.file.util.excel.UserExcelDataWriter;import org.springframework.stereotype.Service;import javax.annotation.Resource;@Servicepublic class UserService implements IUserService {@Resourcepublic ExportService exportService;@Overridepublic FileVO export(UserExportDTO upd, String type){//用工具类生成word文件if (StrUtil.equals(FileTypeEnum.WORD.getType(),type) || StrUtil.equals(FileTypeEnum.PDF.getType(),type)){return exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_XML.getTemplate(),null);}return exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_EXCEL.getTemplate(),new UserExcelDataWriter());}}

ExportService

package com.wwt.file.service;import cn.hutool.core.util.StrUtil;import com.wwt.file.entity.vo.FileVO;import com.wwt.file.enums.FileTypeEnum;import com.wwt.file.enums.UserTemplate;import com.wwt.file.util.DocUtil;import com.wwt.file.util.excel.ExcelDataWriter;import com.wwt.file.util.FileUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.io.File;import java.util.Map;@Slf4j@Servicepublic class ExportService {/*** 临时生成文件的路径*/private static final String TEMP_FILE_DIR = "tmp\\";public FileVO export(Map<String, Object> dataMap, String type ,String tempFileName,ExcelDataWriter writer){if (StrUtil.equals(FileTypeEnum.WORD.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_XML.getChName(),exportWord(dataMap,tempFileName));}if (StrUtil.equals(FileTypeEnum.PDF.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_XML.getChName(),exportPdf(dataMap,tempFileName));}if (StrUtil.equals(FileTypeEnum.EXCEL.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_EXCEL.getChName(),exportExcel(dataMap,writer));}return new FileVO(null,null);}/*** @param dataMap 填充的数据,每个值都不能为null,可以为empty* @param tempFileName 模板文件名,放置在template目录下* @return 文件服务器返回的fileId* @description 模板导出word**/public Long exportWord(Map<String, Object> dataMap, String tempFileName) {//生成文件名String fileName = FileUtil.generateFileName(FileTypeEnum.WORD);DocUtil.saveWord(tempFileName, fileName, dataMap);//获取文件绝对路径String filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,fileName);//上传文件到文件服务器//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//删除生成的文件//FileUtil.delDir(filePath);//模拟上传成功返回的idreturn 1234L; //fileId}/*** @param dataMap 填充的数据,每个值都不能为null,可以为empty* @param tempFileName 模板文件名,放置在template目录下* @return 文件服务器返回的fileId* @description 模板导出pdf**/public Long exportPdf(Map<String, Object> dataMap, String tempFileName) {//生成文件名String fileName = FileUtil.generateFileName(FileTypeEnum.PDF);DocUtil.savePdf(tempFileName, fileName, dataMap);String filePath = new File(FileUtil.getResourceBasePath(), TEMP_FILE_DIR + fileName).getAbsolutePath();//上传文件到文件服务器//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//FileUtil.delDir(filePath);return 1234L; //fileId}/*** 导出excel* 包括生成文档和上传文件* @param dataMap 数据* @param writer 不同的writer实现类处理不同的模板写入* @return*/public Long exportExcel(Map<String, Object> dataMap,ExcelDataWriter writer){writer.export(dataMap);return 1234L;}}

DocUtil

package com.wwt.file.util;import cn.hutool.core.io.IoUtil;import cn.hutool.core.util.StrUtil;import cn.hutool.system.OsInfo;import cn.hutool.system.SystemUtil;import com.aspose.words.Document;import com.aspose.words.FontSettings;import com.aspose.words.License;import com.aspose.words.SaveFormat;import com.wwt.file.enums.FileTypeEnum;import freemarker.template.Template;import lombok.extern.slf4j.Slf4j;import java.io.*;import java.nio.charset.StandardCharsets;import java.nio.file.Files;import java.util.Map;@Slf4jpublic class DocUtil {/*** 临时生成文件的路径*/private static final String TEMP_FILE_DIR = "tmp\\";private static final String LICENSE_FILENAME = "aspose_license.xml";/*** @param templateFileName 模板文件名称* @param fileName生成的文件名.doc结尾* @param dataMap数据,* @description 用模板生成word, 模板存放路径在resources/template下,生成的文件路径在jar包的同级目录/tmp路径下* @attention dataMap中的数据不能为null, 可用空值代替* @other 导出的doc文件其实是文本文件, 而docx文件是二进制文件(其实是一个压缩包).关于如何导出docx可参考 /wantLight/article/details/106105416**/public static void saveWord(String templateFileName, String fileName, Map<String, Object> dataMap) {Writer out = null;try {//根据配置获取FreeMarker模板对象Template template = FreeMarkerTemplateFactory.getTemplate(templateFileName);String filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,fileName);//如果文件父目录不存在,则创建父目录FileUtil.mkParentDirIfNotExist(filePath);// 创建一个Word文档的输出流out = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(new File(filePath).toPath()), StandardCharsets.UTF_8));template.process(dataMap, out);out.flush();} catch (Exception e) {log.error("saveWord error:{}", e.getMessage(), e);} finally {IoUtil.close(out);}}/*** @param templateFileName 模板文件名称* @param fileName 生成的文件名.pdf结尾* @param dataMap数据,* @description 用模板先生成生成word, 再转成pdf,模板存放路径在resources/template下,生成的文件路径在jar包的同级目录/tmp路径下* @attention dataMap中的数据不能为null, 可用空值代替**/public static void savePdf(String templateFileName, String fileName, Map<String, Object> dataMap) {InputStream docInputStream = null;OutputStream outputStream = null;try {String docFileName = fileName.replace(FileTypeEnum.PDF.getSuffix(), FileTypeEnum.WORD.getSuffix());//先生成wordsaveWord(templateFileName, docFileName, dataMap);String docFilePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,docFileName);//aspose将word转成pdfif (!getLicense()) {// 验证License 若不验证则转化出的pdf文档会有水印产生return;}OsInfo osInfo = SystemUtil.getOsInfo();//linux环境的字体,需要将c:\windows\fonts下的字体文件放到指定目录下,否则会乱码if (osInfo.isLinux()) {FontSettings.setFontsFolder("/usr/share/fonts/chinese", true);}docInputStream = Files.newInputStream(new File(docFilePath).toPath());File outputFile = new File(FileUtil.getResourceBasePath(), "tmp/" + fileName);outputStream = Files.newOutputStream(outputFile.toPath());Document doc = new Document(docInputStream);//sourcerFile是将要被转化的word文档doc.save(outputStream, SaveFormat.PDF);//全面支持DOC, DOCX, OOXML, RTF HTML, OpenDocument, PDF, EPUB, XPS, SWF 相互转换//删除生成的临时word文件FileUtil.delDir(docFilePath);} catch (Exception e) {log.error("savePdf error:{}", e.getMessage(), e);} finally {IoUtil.close(docInputStream);IoUtil.close(outputStream);}}/*** 判断是否有授权文件 如果没有则会认为是试用版,转换的文件会有水印** @return*/public static boolean getLicense() {boolean result = false;try {InputStream is = DocUtil.class.getClassLoader().getResourceAsStream(LICENSE_FILENAME);License asposeLic = new License();asposeLic.setLicense(is);result = true;} catch (Exception e) {log.error("getLicense error:{}", e.getMessage());}return result;}}

测试

请求地址: http://localhost:8080/user/export?type=pdf

数据同word

生成excel

原理

使用的是easyExcel原理也是生成生成Excel文件,然后上传文件服务器,然后返回文件id,再删除临时文件因为excel的处理不再是模板不同,构建不同的dataMap即可生成。而是对于每种模板,都要相应地设计不同的写入方式,所以这里我引入了自定义的数据处理抽象类 ExcelDataWriter 。我们处理不同的模板,就构建其子类,实现抽象方法即可。写数据的时候,基本对象信息,列表信息是要分次写入的,列表数据如果过大,还能分批写入,easyExcel会提供文件缓存。

步骤

依赖

<!--转excel--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.1</version></dependency><!--这个注意要加,我在看官网的时候没看到,只加了上面那个,结果后来实测就少了东西报错了--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel-core</artifactId><version>3.1.1</version></dependency>

添加excel模板

Service

UserService

@Servicepublic class UserService implements IUserService {@Resourcepublic ExportService exportService;@Overridepublic FileVO export(UserExportDTO upd, String type){//用工具类生成word文件if (StrUtil.equals(FileTypeEnum.WORD.getType(),type) || StrUtil.equals(FileTypeEnum.PDF.getType(),type)){return exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_XML.getTemplate(),null);}return exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_EXCEL.getTemplate(),new UserExcelDataWriter());}}

ExportService

package com.wwt.file.service;import cn.hutool.core.util.StrUtil;import com.wwt.file.entity.vo.FileVO;import com.wwt.file.enums.FileTypeEnum;import com.wwt.file.enums.UserTemplate;import com.wwt.file.util.DocUtil;import com.wwt.file.util.excel.ExcelDataWriter;import com.wwt.file.util.FileUtil;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import java.io.File;import java.util.Map;@Slf4j@Servicepublic class ExportService {/*** 临时生成文件的路径*/private static final String TEMP_FILE_DIR = "tmp\\";public FileVO export(Map<String, Object> dataMap, String type ,String tempFileName,ExcelDataWriter writer){if (StrUtil.equals(FileTypeEnum.WORD.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_XML.getChName(),exportWord(dataMap,tempFileName));}if (StrUtil.equals(FileTypeEnum.PDF.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_XML.getChName(),exportPdf(dataMap,tempFileName));}if (StrUtil.equals(FileTypeEnum.EXCEL.getType(),type)){return new FileVO(UserTemplate.USER_TEMPLATE_EXCEL.getChName(),exportExcel(dataMap,writer));}return new FileVO(null,null);}/*** @param dataMap 填充的数据,每个值都不能为null,可以为empty* @param tempFileName 模板文件名,放置在template目录下* @return 文件服务器返回的fileId* @description 模板导出word**/public Long exportWord(Map<String, Object> dataMap, String tempFileName) {//生成文件名String fileName = FileUtil.generateFileName(FileTypeEnum.WORD);DocUtil.saveWord(tempFileName, fileName, dataMap);//获取文件绝对路径String filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,fileName);//上传文件到文件服务器//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//删除生成的文件//FileUtil.delDir(filePath);//模拟上传成功返回的idreturn 1234L; //fileId}/*** @param dataMap 填充的数据,每个值都不能为null,可以为empty* @param tempFileName 模板文件名,放置在template目录下* @return 文件服务器返回的fileId* @description 模板导出pdf**/public Long exportPdf(Map<String, Object> dataMap, String tempFileName) {//生成文件名String fileName = FileUtil.generateFileName(FileTypeEnum.PDF);DocUtil.savePdf(tempFileName, fileName, dataMap);String filePath = new File(FileUtil.getResourceBasePath(), TEMP_FILE_DIR + fileName).getAbsolutePath();//上传文件到文件服务器//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//FileUtil.delDir(filePath);return 1234L; //fileId}/*** 导出excel* 包括生成文档和上传文件* @param dataMap 数据* @param writer 不同的writer实现类处理不同的模板写入* @return*/public Long exportExcel(Map<String, Object> dataMap,ExcelDataWriter writer){String fileName = writer.export(dataMap);String filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,fileName);//文件上传逻辑自己实现//Long fileId = fileManagerService.uploadFile(filePath, fileName, "temp", ttl);//FileUtil.delDir(filePath);return 1234L;}}

excel的处理逻辑类
ExcelDataWriter

package com.wwt.file.util.excel;import cn.hutool.core.io.resource.ClassPathResource;import cn.hutool.core.util.StrUtil;import com.wwt.file.enums.FileTypeEnum;import com.wwt.file.util.FileUtil;import lombok.Data;import java.util.Map;@Datapublic abstract class ExcelDataWriter {/*** 模板名* template.xlsx* 子类构造方法去定义*/private String template;/*** 模板所在目录路径* /xxx/xxx/xxx/target/classes/template/*/private String tempBasePath;/*** 模板绝对路径* /xxx/xxx/xxx/target/classes/template/template.xlsx*/private String templatePath;/*** 生成的文件名* aaa.xlsx*/private String fileName;/*** 生成的文件的绝对路径* /xxx/xxx/xxx/aaa.xlsx*/private String filePath;//抽象方法,让子类实现怎么根据dataMap去写数据生成文件,返回文件名字public abstract String export(Map<String, Object> dataMap);/*** 初始化参数*/public ExcelDataWriter(String template) {//D:\code\javaCode\FileHandle\target\classesClassPathResource classPathResource = new ClassPathResource("");String classPath = classPathResource.getFile().getAbsolutePath();this.template = template;//D:\code\javaCode\FileHandle\target\classes\template\this.tempBasePath = StrUtil.concat(true,classPath,"\\template\\");//D:\code\javaCode\FileHandle\target\classes\template\xxx.xlsxthis.templatePath = StrUtil.concat(true,tempBasePath,template);this.fileName = FileUtil.generateFileName(FileTypeEnum.EXCEL);//D:\code\javaCode\FileHandle\tmp\123456.xlsxthis.filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),"tmp\\",this.fileName);//写数据前要保证父目录一定要存在FileUtil.mkParentDirIfNotExist(this.filePath);}}

UserExcelDataWriter

package com.wwt.file.util.excel;import cn.hutool.core.map.MapUtil;import cn.hutool.core.util.ObjectUtil;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.ExcelWriter;import com.alibaba.excel.enums.WriteDirectionEnum;import com.alibaba.excel.write.metadata.WriteSheet;import com.alibaba.excel.write.metadata.fill.FillConfig;import com.alibaba.excel.write.metadata.fill.FillWrapper;import com.wwt.file.entity.User;import com.wwt.file.enums.UserTemplate;import lombok.extern.slf4j.Slf4j;import java.util.HashMap;import java.util.List;import java.util.Map;/*** 用户excel模板生成处理类*/@Slf4jpublic class UserExcelDataWriter extends ExcelDataWriter{//指定模板名private final String template = UserTemplate.USER_TEMPLATE_EXCEL.getTemplate();/*** 初始化参数**/public UserExcelDataWriter() {super(UserTemplate.USER_TEMPLATE_EXCEL.getTemplate());}/*** 导出文件到本地* 返回文件的绝对路径* @param dataMap* @return*/@Overridepublic String export(Map<String, Object> dataMap) {User user1 = MapUtil.get(dataMap, "user1", User.class);User user2 = MapUtil.get(dataMap, "user2", User.class);List<User> user3 = MapUtil.get(dataMap, "users", List.class);List<User> user4 = MapUtil.get(dataMap, "users", List.class);List<User> user5 = MapUtil.get(dataMap, "users", List.class);String templateFileName = super.getTemplatePath();String filePath = super.getFilePath();ExcelWriter excelWriter = null;String fileName = null;try{//技巧,数据分段写入excelWriter = EasyExcel.write(filePath).withTemplate(templateFileName).build();WriteSheet writeSheet = EasyExcel.writerSheet().build();HashMap<String, Object> baseMessMap = MapUtil.newHashMap();HashMap<String, Object> user3ListMap = MapUtil.newHashMap();HashMap<String, Object> user4ListMap = MapUtil.newHashMap();//简单模板数据写入baseMessMap.put("name1",user1.getName());baseMessMap.put("age1",user1.getAge());baseMessMap.put("email1",user1.getEmail());baseMessMap.put("id1",user1.getId());baseMessMap.put("name2",user2.getName());baseMessMap.put("age2",user2.getAge());baseMessMap.put("email2",user2.getEmail());baseMessMap.put("id2",user2.getId());excelWriter.fill(baseMessMap, writeSheet);//横向列表数据写入FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();// 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data5,然后多个list必须用 FillWrapper包裹excelWriter.fill(new FillWrapper("user5", user5), fillConfig, writeSheet);//纵向列表数据写入 如果数据量很大,可以看看官网,有让你分段查询插入数据的办法,会用到缓存,效率还是很高的excelWriter.fill(new FillWrapper("user3", user3), writeSheet);excelWriter.fill(new FillWrapper("user4", user4), writeSheet);fileName = super.getFileName();}catch (Exception e){log.error("excel create error : ",e.getMessage(), e);}finally {if (ObjectUtil.isNotNull(excelWriter)){excelWriter.finish();}}return fileName;}}

测试

请求地址:http://localhost:8080/user/export?type=excel

数据同word,只是构建方法不一样,看上面的 UserExcelDataWriter 的 export

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。