一、FastExcel简介
FastExcel
是由原EasyExcel作者在阿里巴巴宣布停止维护EasyExcel之后推出的升级版框架。它继承了EasyExcel的所有优点,并且在性能和功能上进行了显著的提升和创新。
官网:FastExcel官网
参考文章:FastExcel:超越EasyExcel的新一代Excel处理工具
1.1 主要特点
完全兼容原 EasyExcel 的所有功能和特性
,这使得用户可以无缝过渡
。- 从 EasyExcel 迁移到 FastExcel 只需简单地更换包名和 Maven 依赖即可完成升级。
- 在功能上,比 EasyExcel 提供更多创新和改进。
- FastExcel 1.0.0 版本新增了读取 Excel 指定行数和将 Excel 转换为 PDF 的功能。
二、实战教学
2.1 添加依赖
<!-- fastexcel -->
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.0.0</version> <!-- 请确保使用最新版本 -->
</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</artifactId>
</dependency>
2.2 创建实体类
在使用FastExcel
进行Excel文件的读写操作之前,需要定义一个实体类,该类中的每个属性对应Excel中的一列。使用@ExcelProperty
注解来指定列名。
@Data
@NoArgsConstructor //无参构造
@AllArgsConstructor //全参构造
@Builder
public class ImportEmp {
@ExcelProperty(value = "序号", index = 0)
private Long id;
@ExcelProperty(value = "部门名称", index = 1)
private String deptName;
@ExcelProperty(value = "用户名", index = 2)
private String username;
@ExcelProperty(value = "姓名", index = 3)
private String name;
@ExcelProperty(value = "性别", index = 4)
private String gender;
@ExcelProperty(value = "年龄", index = 5)
private Integer age;
@ExcelProperty(value = "职位名称", index = 6)
private String jobTitle;
@ExcelProperty(value = "入职时间", index = 7)
private LocalDate entryDate;
@ExcelProperty(value = "联系电话", index = 8)
private String phone;
@ExcelProperty(value = "电子邮箱", index = 9)
private String email;
@ExcelProperty(value = "角色名称", index = 10)
private String roleName;
}
2.3 创建监听器
FastExcel
通过事件监听器实现Excel文件的逐行读取,这对于处理大文件尤为重要,因为它可以避免内存溢出的问题。下面是一个事件监听器的示例,它在读取每行数据时将数据添加到列表中,并在所有数据读取完成后执行一些操作。
@Slf4j
public class BaseExcelListener<T> extends AnalysisEventListener<T> {
// 用于存储读取到的Excel数据对象列表
private List<T> dataList = new ArrayList<>();
@Override
public void invoke(T t, AnalysisContext analysisContext) {
log.info("读取到一条数据:{}", JSON.toJSONString(t));
// 每读取一行数据,就将其添加到dataList中
dataList.add(t);
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 当所有数据读取完成后,可以在这里进行一些后续操作,如打印读取到的数据数量
log.info("读取完成,共读取了 {} 条数据", dataList.size());
}
// 提供一个方法用于获取存储数据的列表
public List<T> getDataList() {
return dataList;
}
}
2.4 实现读取功能
以下是使用FastExcel
进行Excel读取
的示例代码。通过FastExcel.read
方法读取Excel文件,并使用之前创建的监听器来处理读取到的数据。
2.4.1 后端实现
Controller层
@RestController
@RequestMapping("/emp")
@Tag(name = "员工管理接口")
@Slf4j
@RequiredArgsConstructor
public class EmpController {
private final EmpService empService;
@PostMapping("/import")
@Operation(summary = "Excel导入")
@PreAuthorize("hasAuthority('ems:employee:import')")
public Result importEmp(@RequestParam("file") MultipartFile file){
log.info("Excel导入,文件:{}", file.getOriginalFilename());
empService.importEmp(file);
return Result.success();
}
}
Service层
/**
* Excel导入员工
* @param file Excel文件
*/
public void importEmp(MultipartFile file) {
if (file.isEmpty()) {
throw new RuntimeException("文件为空,请选择一个文件上传!");
}
try {
BaseExcelListener<ImportEmp> baseExcelListener = new BaseExcelListener<>();
FastExcel.read(file.getInputStream(), ImportEmp.class,
baseExcelListener).sheet().doRead();
// 得到读取数据
List<ImportEmp> dataList = baseExcelListener.getDataList();
// 存入数据库操作,此处省略,直接打印输出
System.out.println(dataList);
} catch (IOException e) {
throw new RuntimeException("文件读取失败!");
}
}
2.4.2 前端实现
API封装
// src/api/emp.js
// 导入员工
export const importEmpService = (file) => {
const formData = new FormData()
formData.append('file', file)
return request({
url: '/emp/import',
method: 'post',
headers: {
'Content-Type': 'multipart/form-data'
},
data: formData,
timeout: 60000 // 设置较长的超时时间
})
}
上传组件实现
<!-- 导入对话框 -->
<el-dialog
title="导入员工"
v-model="importDialogVisible"
width="400px"
:close-on-click-modal="false"
destroy-on-close
>
<el-upload
class="upload-demo"
drag
:action="uploadUrl"
:headers="uploadHeaders"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:before-upload="beforeUpload"
:show-file-list="true"
:limit="1"
accept=".xlsx,.xls"
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">
将文件拖到此处,或<em>点击上传</em>
</div>
<template #tip>
<div class="el-upload__tip">
只能上传xlsx/xls文件,且不超过5MB
<el-link
type="primary"
:underline="false"
style="margin-left: 10px"
@click="downloadTemplate"
>
下载模板
</el-link>
</div>
</template>
</el-upload>
</el-dialog>
上传相关方法
// 上传相关配置
const uploadUrl = '/api/emp/import'
const uploadHeaders = computed(() => ({
Authorization: useTokenStore().token
}))
// 上传前校验
const beforeUpload = (file) => {
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.type === 'application/vnd.ms-excel'
const isLt5M = file.size / 1024 / 1024 < 5
if (!isExcel) {
ElMessage.error('只能上传 Excel 文件!')
return false
}
if (!isLt5M) {
ElMessage.error('文件大小不能超过 5MB!')
return false
}
return true
}
// 上传成功处理
const handleUploadSuccess = (response) => {
if (response.code === 1) {
ElMessage.success(response.message || '导入成功')
importDialogVisible.value = false
getEmployeeList()
} else {
ElMessage.error(response.message || '导入失败')
}
}
// 上传失败处理
const handleUploadError = (error) => {
console.error('上传失败:', error)
ElMessage.error('上传失败,请稍后重试')
}
2.5 实现写入功能
以下是使用FastExcel
进行Excel写入的示例代码。首先,创建测试数据,然后通过FastExcel.write
方法将数据写入到Excel文件中。
2.5.1 后端实现
@RestController
@RequestMapping("/emp")
@Tag(name = "员工管理接口")
@Slf4j
@RequiredArgsConstructor
public class EmpController {
private final EmpService empService;
@GetMapping("/template")
@Operation(summary = "下载Excel员工表模版")
@PreAuthorize("hasAuthority('ems:employee:export')")
public void downloadTemplate(HttpServletResponse response) throws IOException {
log.info("下载Excel员工表模版");
// 设置响应头
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("员工表", StandardCharsets.UTF_8);
response.setHeader("Content-disposition",
"attachment;filename*=utf-8''" + fileName + ".xlsx");
// 写入示例数据
FastExcel.write(response.getOutputStream(), ImportEmp.class)
.sheet("模板")
.doWrite(buildData());
}
// 构建示例数据
private List<ImportEmp> buildData() {
ImportEmp emp1 = ImportEmp.builder()
.id(1L)
.deptName("研发部")
.username("admin")
.name("张三")
.gender("男")
.age(18)
.jobTitle("Java开发")
.entryDate(LocalDate.of(2023, 1, 1))
.phone("13800000000")
.email("admin@example.com")
.roleName("管理员")
.build();
return new ArrayList<>(List.of(emp1));
}
}
2.5.2 前端实现
API封装
// src/api/emp.js
// 导出员工
export const exportEmpService = (params) => {
return request.get('/emp/export', {
params,
responseType: 'blob'
})
}
模板下载实现
// 下载模板
const downloadTemplate = async () => {
try {
const response = await fetch('/api/emp/template', {
method: 'GET',
headers: {
'Authorization': useTokenStore().token
}
})
if (!response.ok) throw new Error('下载失败')
const blob = await response.blob()
if (blob.size === 0) {
throw new Error('下载的文件为空')
}
// 从响应头中获取文件名
const contentDisposition = response.headers.get('content-disposition')
let filename = '员工导入模板.xlsx'
if (contentDisposition) {
const filenameMatch = contentDisposition.match(/filename\*=utf-8''(.+)/)
if (filenameMatch) {
filename = decodeURIComponent(filenameMatch[1])
}
}
// 创建下载链接并触发下载
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
ElMessage.success('下载成功')
} catch (error) {
console.error('下载模板失败:', error)
ElMessage.error('下载模板失败,请重试')
}
}
前端注意事项:
- 文件上传时需要设置正确的Content-Type
- 下载Excel文件时需要正确处理二进制流
- 大文件上传需要设置较长的超时时间
- 注意处理文件名的编码问题
- 添加适当的错误处理和用户提示
2.6 Excel转换为PDF
FastExcel
还支持将Excel文件转换为PDF文件,这一功能底层依赖于Apache POI
和itext-pdf
。请注意,使用itext-pdf
时需要确保符合其许可证要求。
示例对象
FastExcel.convertToPdf(new File("excelFile"),new File("pdfFile"),null,null);
2.7 EasyExcel 如何升级到FastExcel
2.7.1 修改依赖
将 EasyExcel 的依赖替换为 FastExcel 的依赖,如下:
<!-- easyexcel 依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>xxxx</version>
</dependency>
依赖替换为:
<dependency>
<groupId>cn.idev.excel</groupId>
<artifactId>fastexcel</artifactId>
<version>1.0.0</version>
</dependency>
2.7.2 修改包名
将 EasyExcel 的包名替换为 FastExcel 的包名,如下:
// 将 easyexcel 的包名替换为 FastExcel 的包名
import com.alibaba.excel.**;
import cn.idev.excel.**;
小结
FastExcel
作为一个高效且易于使用的Excel处理工具,不仅继承了EasyExcel的所有优点,还在此基础上进行了性能和功能的增强。通过上述示例,我们可以看到FastExcel如何简化Excel文件的读写操作,以及如何通过事件监听器实现流式处理,从而有效管理内存使用。无论是企业数据导入导出还是个人项目开发,FastExcel都能提供强大的支持。
FastExcel与EasyExcel的区别
- 性能提升:FastExcel在性能上比EasyExcel更好,更稳定。
- API一致性:FastExcel与EasyExcel的API完全一致,可以无缝切换。
- 功能增加:FastExcel 1.0.0版本新增了读取Excel指定行数和将Excel转换为PDF的功能。