页面上有一份表单,包含基本信息和列表信息,用户填写完信息后,点击预览/导出,可以预览/导出word、pdf、excel文档。
因为代码重构过,所以看流程可能会有点绕,为方便能看懂,这里直接提供了项目的地址
文件生成项目
期望你在本文能收获什么
一套比较完整的文件生成的解决方案和思路
一些代码构建的思路和细节,文中代码的一些组成是我参考在用项目的结构,重构改过来的,用上了一些设计模式,扩展性、规范性应该也还是可以的。
pdf效果一样
- 有的项目组做项目喜欢上传/生成文件再上传后在数据库存文件信息,然后返回文件地址给前端,前端拿到文件地址就直接处理
- 有的项目组做项目喜欢上传/生成文件再上传后在数据库存文件信息,然后返回文件id给前端,前端默认是要知道文件服务器的基础访问地址,所以让前端拿到文件id后他们就会处理了(拼接字符串为文件访问地址(文件id为生成文件名)如 “http://文件地址/文件id”,或者再发个请求到数据库查文件具体信息(拿到地址)都可以)
- 本文使用的是第二种。单独有文件服务器做文件上传,然后把上传成功的文件的基本信息保存到数据库,然后返回文件id给前端。
- 本文主要是介绍如何生成各种类型的文档,所以不包含如何上传文件部分。当然市面上常用的文件上传服务实现这么多,相信你也是可以找到合适的解决方案的。
- 如果以后有空,再给补一个通用的文件上传服务好了,哈哈
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>org.wwt.filegroupId>
<artifactId>FileHandleartifactId>
<version>1.0-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.1.5.RELEASEversion>
parent>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.5.2version>
dependency>
<dependency>
<groupId>aspose-wordsgroupId>
<artifactId>aspose-wordsartifactId>
<version>15.8.0version>
<scope>systemscope>
<systemPath>${project.basedir}/src/main/resources/lib/aspose-words-15.8.0-jdk16.jarsystemPath>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcel-coreartifactId>
<version>3.1.1version>
dependency>
dependencies>
project>
#mysql数据库连接
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/xxx?serverTimezone=GMT%2B8
username: root
password: 123456
这里看到我用了mysql,其实我这里数据是由前端传过来的,不是查出来的,所以没用到数据库。只是加个配置项目启动就不会找默认配置报错。当然你可以的通过加个注解的方式解决,这里不提了。
我把包名也复制进去,希望尽量能让你还原项目
package com.wwt.file.entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
FileVO - 文件返回信息
package com.wwt.file.entity.vo;
import lombok.Data;
/**
* 封装文件返回信息
*
*/
@Data
public 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;
/**
* 封装前端传过来的用户请求信息
*/
@Data
public 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;
}
}
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 ,直接在这些显示文件类型信息的地方都用字符串写死
package com.wwt.file.result;
import com.wwt.file.result.enums.ResultCodeEnum;
import lombok.Data;
@Data
public 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
*/
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
@ToString
public 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, 22003, "阿里云视频文件删除失败"),
FETCH_VIDEO_UPLOADAUTH_ERROR(false, 22004, "获取上传地址和凭证失败"),
REFRESH_VIDEO_UPLOADAUTH_ERROR(false, 22005, "刷新上传地址和凭证失败"),
FETCH_PLAYAUTH_ERROR(false, 22006, "获取播放凭证失败"),
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
*/
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
@ToString
public 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 类即可
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());
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-freemarkerartifactId>
dependency>
另存为xml文件
把文件模板放进template目录
用notepad++之类的工具类打开并修改模板文件
实体类数据
小提示:如果文件内容不是格式化的,你复制内容到idea里的随便一个xml文档,ctrl+alt+L代码格式化之后再复制回来也是可以的。
为什么先预写数据,然后再查找替换。而不是在word文档里面直接写${xxx.xxx},然后生成xml文件呢?因为实测过可能$符号会因为一些格式问题,预写替换符再成xml的话会出奇怪的问题,比如说$符号和属性分开了,导致数据填不进去,或者报错。
list数据
全部替换后,转为word文档打开应该是如下面这个样子的。但是因为加了list标签的原因,可能替换完就打不开了。不过还是再看看替换完应该是长什么样的吧。
{
"user1": {
"id": 1,
"name": "张三",
"age": 77,
"email": "[email protected]"
},
"user2": {
"id": 19,
"name": "李四",
"age": 68,
"email": "[email protected]"
},
"users": [
{
"id": 33,
"name": "帅哥1",
"age": 18,
"email": "[email protected]"
},
{
"id": 34,
"name": "帅哥2",
"age": 18,
"email": "[email protected]"
},
{
"id": 35,
"name": "帅哥3",
"age": 18,
"email": "[email protected]"
},
{
"id": 36,
"name": "帅哥4",
"age": 18,
"email": "[email protected]"
},
{
"id": 37,
"name": "帅哥5",
"age": 18,
"email": "[email protected]"
},
{
"id": 38,
"name": "帅哥6",
"age": 18,
"email": "[email protected]"
},
{
"id": 39,
"name": "帅哥7",
"age": 18,
"email": "[email protected]"
},
{
"id": 40,
"name": "帅哥8",
"age": 18,
"email": "[email protected]"
}
]
}
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 {
@Resource
private IUserService userService;
@PostMapping("/export")
public Result<FileVO> export(@RequestBody UserExportDTO upd, String type){
return FileResult.setResult(FileResultEnum.UPLOAD_SUCCESS).data(userService.export(upd,type));
}
}
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);
}
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;
@Service
public class UserService implements IUserService {
@Resource
public ExportService exportService;
@Override
public 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的话要提供excelwriter
return exportService.export(upd.structDataMap(), type, UserTemplate.USER_TEMPLATE_EXCEL.getTemplate(),new UserExcelDataWriter());
}
}
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
@Service
public 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 中,实现上传和生成的逻辑解耦。
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;
@Slf4j
public 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可参考 https://blog.csdn.net/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.doc
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());
//先生成word
saveWord(templateFileName, docFileName, dataMap);
String docFilePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,docFileName);
//aspose将word转成pdf
if (!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": "[email protected]"
},
"user2": {
"id": 19,
"name": "李四",
"age": 68,
"email": "[email protected]"
},
"users": [
{
"id": 33,
"name": "帅哥1",
"age": 18,
"email": "[email protected]"
},
{
"id": 34,
"name": "帅哥2",
"age": 18,
"email": "[email protected]"
},
{
"id": 35,
"name": "帅哥3",
"age": 18,
"email": "[email protected]"
},
{
"id": 36,
"name": "帅哥4",
"age": 18,
"email": "[email protected]"
},
{
"id": 37,
"name": "帅哥5",
"age": 18,
"email": "[email protected]"
},
{
"id": 38,
"name": "帅哥6",
"age": 18,
"email": "[email protected]"
},
{
"id": 39,
"name": "帅哥7",
"age": 18,
"email": "[email protected]"
},
{
"id": 40,
"name": "帅哥8",
"age": 18,
"email": "[email protected]"
}
]
}
地址: https://repository.aspose.com/repo/com/aspose/
进去后选一个版本下即可
把jar包添加到项目中
<dependency>
<groupId>aspose-wordsgroupId>
<artifactId>aspose-wordsartifactId>
<version>15.8.0version>
<scope>systemscope>
<systemPath>${project.basedir}/src/main/resources/lib/aspose-words-15.8.0-jdk16.jarsystemPath>
dependency>
注意版本和jar包名字要对应的上
<License>
<Data>
<Products>
<Product>Aspose.Total for JavaProduct>
<Product>Aspose.Words for JavaProduct>
Products>
<EditionType>EnterpriseEditionType>
<SubscriptionExpiry>20991231SubscriptionExpiry>
<LicenseExpiry>20991231LicenseExpiry>
<SerialNumber>8bfe198c-7f0c-4ef8-8ff0-acc3237bf0d7SerialNumber>
Data>
<Signature>
sNLLKGMUdF0r8O1kKilWAGdgfs2BvJb/2Xp8p5iuDVfZXmhppo+d0Ran1P9TKdjV4ABwAgKXxJ3jcQTqE/2IRfqwnPf8itN8aFZlV3TJPYeD3yWE7IT55Gz6EijUpC7aKeoohTb4w2fpox58wWoF3SNp6sK6jDfiAUGEHYJ9pjU=
Signature>
License>
没有改变,因为都是通过 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;
@Service
public class UserService implements IUserService {
@Resource
public ExportService exportService;
@Override
public 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());
}
}
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
@Service
public 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;
}
}
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;
@Slf4j
public 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可参考 https://blog.csdn.net/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());
//先生成word
saveWord(templateFileName, docFileName, dataMap);
String docFilePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),TEMP_FILE_DIR,docFileName);
//aspose将word转成pdf
if (!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
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>3.1.1version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcel-coreartifactId>
<version>3.1.1version>
dependency>
@Service
public class UserService implements IUserService {
@Resource
public ExportService exportService;
@Override
public 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());
}
}
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
@Service
public 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){
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;
}
}
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;
@Data
public 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\classes
ClassPathResource 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.xlsx
this.templatePath = StrUtil.concat(true,tempBasePath,template);
this.fileName = FileUtil.generateFileName(FileTypeEnum.EXCEL);
//D:\code\javaCode\FileHandle\tmp\123456.xlsx
this.filePath = StrUtil.concat(true,FileUtil.getResourceBasePath(),"tmp\\",this.fileName);
//写数据前要保证父目录一定要存在
FileUtil.mkParentDirIfNotExist(this.filePath);
}
}
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模板生成处理类
*/
@Slf4j
public class UserExcelDataWriter extends ExcelDataWriter{
//指定模板名
private final String template = UserTemplate.USER_TEMPLATE_EXCEL.getTemplate();
/**
* 初始化参数
*
*/
public UserExcelDataWriter() {
super(UserTemplate.USER_TEMPLATE_EXCEL.getTemplate());
}
/**
* 导出文件到本地
* 返回文件的绝对路径
* @param dataMap
* @return
*/
@Override
public 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