本文最后更新于108 天前,其中的信息可能已经过时,如有错误请发送邮件到1770087309@qq.com
1、EasyExcel介绍
传统操作Excel大多都是利用Apach POI进行操作的,但是POI框架并不完善,使用过程非常繁琐且有较多的缺陷:
- 动态操作Excel非常繁琐,对于新手来说,很难在短时间内上手;
- 读写时需要占用较大的内存,当数据量大时容易发生内存溢出问题(OOM);
基于上述原因,阿里开源出一款易于上手,且比较节省内存的Excel框架:EasyExcel
注意:easyExcel底层也是使用POI实现的;
2、EasyExcel导出数据快速入门
2.1 依赖引入
<!--引入easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.3.4</version>
</dependency>
2.2 构建测试实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User implements Serializable {
private String userName;
private Integer age;
private String address;
private Date birthday;
}
2.3 数据导出到excel
@SpringBootTest
class EasyexcelApplicationTests {
public List<User> init() {
//组装数据
ArrayList<User> users = new ArrayList<>();
for (int i = 0; i < 10; i++) {
User user = new User();
user.setAddress("上海" + i);
user.setUserName("张三" + i);
user.setBirthday(new Date());
user.setAge(10 + i);
users.add(user);
}
return users;
}
/**
* 直接导出后,表头名称默认是实体类中的属性名称
*/
@Test
void contextLoads() {
List<User> users = init();
//不做任何注解处理时,表头名称与实体类属性名称一致
EasyExcel.write("C:\\Users\\KCH\\Desktop\\ex\\用户.xls", User.class).sheet("用户信息").doWrite(users);
}
}
2.4 自定义表头
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
/**
* 通过注解自定义表头名称 注解添加排序规则,值越大 越靠近右边
*/
public class User implements Serializable {
@ExcelProperty(value = {"用户名"},index = 0)
private String userName;
@ExcelProperty(value = {"年龄"},index = 1)
private Integer age;
@ExcelProperty(value = {"地址"} ,index = 3)
private String address;
@ExcelProperty(value = {"生日"},index = 2)
private Date birthday;
}
3、EasyExcel导出数据高级设置
3.1 自定义日期格式
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User implements Serializable {
@ExcelProperty(value = {"用户名"},index = 1)
private String userName;
@ExcelProperty(value = {"年龄"},index = 2)
private Integer age;
@ExcelProperty(value = {"地址"} ,index = 4)
private String address;
@ExcelProperty(value = {"生日"},index = 3)
//注意:日期格式注解由alibaba.excel提供
@DateTimeFormat("yyyy/MM/dd HH:mm")
private Date birthday;
}
3.2 合并表头
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User implements Serializable {
@ExcelProperty(value = {"用户基本信息","用户名"},index = 1)
private String userName;
@ExcelProperty(value = {"用户基本信息","年龄"},index = 2)
private Integer age;
@ExcelProperty(value = {"用户基本信息","地址"} ,index = 4)
private String address;
@ExcelProperty(value = {"用户基本信息","生日"},index = 3)
//注意:日期格式注解由alibaba.excel提供
@DateTimeFormat("yyyy/MM/dd HH:mm")
private Date birthday;
}
3.3 忽略指定表头信息
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User implements Serializable {
@ExcelProperty(value = {"用户基本信息","用户名"},index = 1)
@ExcelIgnore
private String userName;
@ExcelProperty(value = {"用户基本信息","年龄"},index = 2)
private Integer age;
@ExcelProperty(value = {"用户基本信息","地址"} ,index = 4)
private String address;
@ExcelProperty(value = {"用户基本信息","生日"},index = 3)
//注意:日期格式注解由alibaba.excel提供
@DateTimeFormat("yyyy/MM/dd HH:mm")
private Date birthday;
}
3.4 设置单元格大小
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@HeadRowHeight(value = 35) // 表头行高
@ContentRowHeight(value = 25) // 内容行高
@ColumnWidth(value = 50) // 列宽
public class User implements Serializable {
@ExcelProperty(value = {"用户基本信息","用户名"},index = 1)
@ExcelIgnore
private String userName;
@ExcelProperty(value = {"用户基本信息","年龄"},index = 2)
private Integer age;
@ExcelProperty(value = {"用户基本信息","地址"} ,index = 4)
private String address;
@ExcelProperty(value = {"用户基本信息","生日"},index = 3)
//注意:日期格式注解由alibaba.excel提供
@DateTimeFormat("yyyy/MM/dd HH:mm")
private Date birthday;
}
4、EasyExcel导入数据
/**
* excel数据格式必须与实体类定义一致,否则数据读取不到
*/
@Test
public void readExcel() {
ArrayList<User> users = new ArrayList<>();
//读取数据
EasyExcel.read("C:\\Users\\KCH\\Desktop\\ex\\用户.xls", User.class, new AnalysisEventListener<User>() {
//解析excel中的每条数据
@Override
public void invoke(User o, AnalysisContext analysisContext) {
System.out.println(o);
users.add(o);
}
//解析完成后执行的操作
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("完成。。。。");
}
}).sheet().doRead();
System.out.println(users);
}
5、结合阿里云上传保存导出
5.1 阿里云OSS配置
5.1.1 配置文件
sky:
alioss:
endpoint: oss-cn-beijing.aliyuncs.com
access-key-id: LTAI5tPZCZvadQUz5VVqWdSF
access-key-secret: cqrELWRwTQF6uoCUhU7R1enkmL2ppZ
bucket-name: kch-file
5.1.2 配置文件加载
@Component
@ConfigurationProperties(prefix = "sky.alioss")
@Data
public class AliOssProperties {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
}
5.1.3 上传工具类AliOssUtil
@Data
@AllArgsConstructor
@Slf4j
public class AliOssUtil {
private String endpoint;
private String accessKeyId;
private String accessKeySecret;
private String bucketName;
/**
* 文件上传
*
* @param bytes
* @param objectName
* @return
*/
public String upload(byte[] bytes, String objectName) {
// 创建OSSClient实例。
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
try {
// 创建PutObject请求。(上传文件)
ossClient.putObject(bucketName, objectName, new ByteArrayInputStream(bytes));
} catch (OSSException oe) {
System.out.println("捕获到 OSSException,这意味着您的请求已发送到 OSS, 但由于某种原因被拒绝并显示错误响应");
System.out.println("Error Message:" + oe.getErrorMessage());
System.out.println("Error Code:" + oe.getErrorCode());
System.out.println("Request ID:" + oe.getRequestId());
System.out.println("Host ID:" + oe.getHostId());
} catch (ClientException ce) {
System.out.println("捕获到 ClientException,这意味着客户端在尝试与 OSS 通信时遇到了严重的内部问题,例如无法访问网络");
System.out.println("Error Message:" + ce.getMessage());
} finally {
if (ossClient != null) {
ossClient.shutdown();
}
}
//文件访问路径规则 https://BucketName.Endpoint/ObjectName
StringBuilder stringBuilder = new StringBuilder("https://");
stringBuilder
.append(bucketName)
.append(".")
.append(endpoint)
.append("/")
.append(objectName);
log.info("文件上传到:{}", stringBuilder.toString());
return stringBuilder.toString();
}
}
5.1.4 配置类
@Configuration
@Slf4j
public class OssConfiguration {
/**
* 返回阿里云OSS文件上传工具类对象
*
* @param aliOssProperties
* @return
*/
@Bean
@ConditionalOnMissingBean
public AliOssUtil aliOssUtil(AliOssProperties aliOssProperties) {
log.info("开始创建阿里云文件上传工具类对象:{}", aliOssProperties);
return new AliOssUtil(
aliOssProperties.getEndpoint(),
aliOssProperties.getAccessKeyId(),
aliOssProperties.getAccessKeySecret(),
aliOssProperties.getBucketName());
}
}
5.1.5 上传测试
@RestController
@RequestMapping("/admin/upload")
@Slf4j
@Api(tags = "上传接口")
public class UploadController {
@Autowired
private AliOssUtil aliOssUtil;
@PostMapping("/upload")
@ApiOperation("文件上传")
public String upload(MultipartFile file) {
log.info("文件上传:{}", file);
try {
// 前端传过来的文件名
String originalFilename = file.getOriginalFilename();
// 截取文件名的后缀
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
// 重命文件名
String newName = UUID.randomUUID().toString() + suffix;
// 文件的请求路径
return aliOssUtil.upload(file.getBytes(), newName);
} catch (IOException e) {
log.error("文件上传失败:{}", e);
}
return "文件上传失败";
}
}
5.2 简单导出
@GetMapping("/easy-export")
@ApiOperation("简单导出")
public String export() {
// 获取数据
List<GlobalExpressCompany> list = globalExpressCompanyService.list();
// 生成 Excel 文件路径
String rootDir = System.getProperty("user.dir"); // 获取项目根目录
String filePath = rootDir + File.separator + "ex" + File.separator + "公司正则.xls";
File dir = new File(rootDir + File.separator + "ex");
if (!dir.exists()) {
dir.mkdirs(); // 确保目录存在
}
// 写入 Excel 文件
EasyExcel.write(filePath).sheet("快递公司").doWrite(list);
// 上传文件至阿里云 OSS
File file = new File(filePath);
try (FileInputStream fis = new FileInputStream(file)) {
byte[] fileBytes = fis.readAllBytes(); // 获取文件字节数组
String uploadUrl = aliOssUtil.upload(fileBytes, "公司正则.xls");// 上传文件
// 判断上传是否成功
if (uploadUrl != null && !uploadUrl.isEmpty()) {
// 上传成功后删除本地文件
boolean isFileDeleted = file.delete();
if (isFileDeleted) {
System.out.println("本地文件删除成功");
} else {
System.out.println("本地文件删除失败");
}
} else {
System.out.println("文件上传失败");
}
return uploadUrl;
} catch (IOException e) {
e.printStackTrace();
return "上传失败"; // 处理文件读取和上传异常
}
}
5.3 模板填充
@GetMapping("/template-fill")
@ApiOperation("模板填充")
public String exportWithTemplate() {
// 生成 Excel 文件路径
String rootDir = System.getProperty("user.dir"); // 获取项目根目录
String filePath = rootDir + File.separator + "ex" + File.separator + "模板填充.xlsx";
File dir = new File(rootDir + File.separator + "ex");
if (!dir.exists()) {
dir.mkdirs(); // 确保目录存在
}
// 读取模板文件
ClassPathResource classPathResource = new ClassPathResource("template/fillSimple.xlsx");
try (
InputStream inputStream = classPathResource.getInputStream();
ByteArrayOutputStream os = new ByteArrayOutputStream()
) {
// 配置填充的数据(填充 map 和 list)
Map<String, String> map = Map.of("year", "2022");
List<FillSimpleDTO> list = List.of(
new FillSimpleDTO("老张", 1),
new FillSimpleDTO("老李", 2)
);
// 配置填充配置
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
// 使用 EasyExcel 进行模板填充
ExcelWriter excelWriter = EasyExcel.write(os).withTemplate(inputStream).build(); // 读取模板
WriteSheet writeSheet = EasyExcel.writerSheet().build(); // 写到第一个工作表
// 填充模板
excelWriter.fill(map, writeSheet); // 填充单一变量
excelWriter.fill(list, fillConfig, writeSheet); // 填充列表
excelWriter.finish(); // 完成填充
// 获取填充后的字节数组
byte[] fileBytes = os.toByteArray();
// 保存到本地
try (FileOutputStream fos = new FileOutputStream(filePath)) {
// fos.write(fileBytes); // 第一种写入方式,直接写入
EasyExcel.write(fos).sheet("模板填充").doWrite(list); // 第二种写入方式,使用EasyExcel进行写入,write传参可以是输出流,也可以是文件路径
}
// 上传文件至阿里云 OSS
String uploadUrl = aliOssUtil.upload(fileBytes, "模板填充.xlsx");
// 判断上传是否成功
if (uploadUrl != null && !uploadUrl.isEmpty()) {
// 上传成功后删除本地文件
File file = new File(filePath);
boolean isFileDeleted = file.delete();
if (isFileDeleted) {
System.out.println("本地文件删除成功");
} else {
System.out.println("本地文件删除失败");
}
return uploadUrl; // 返回上传的 URL
} else {
System.out.println("文件上传失败");
return "上传失败";
}
} catch (IOException e) {
e.printStackTrace();
return "上传失败"; // 处理文件读取和上传异常
}
}