FastExcel + Vue3实战:10分钟搞定企业级Excel导入导出

一、FastExcel简介

FastExcel是由原EasyExcel作者在阿里巴巴宣布停止维护EasyExcel之后推出的升级版框架。它继承了EasyExcel的所有优点,并且在性能和功能上进行了显著的提升和创新。

官网:FastExcel官网
参考文章:FastExcel:超越EasyExcel的新一代Excel处理工具
在这里插入图片描述

1.1 主要特点

  1. 完全兼容原 EasyExcel 的所有功能和特性,这使得用户可以无缝过渡
  2. 从 EasyExcel 迁移到 FastExcel 只需简单地更换包名和 Maven 依赖即可完成升级。
  3. 在功能上,比 EasyExcel 提供更多创新和改进。
  4. 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('下载模板失败,请重试')
    }
}

在这里插入图片描述

前端注意事项:

  1. 文件上传时需要设置正确的Content-Type
  2. 下载Excel文件时需要正确处理二进制流
  3. 大文件上传需要设置较长的超时时间
  4. 注意处理文件名的编码问题
  5. 添加适当的错误处理和用户提示

2.6 Excel转换为PDF

FastExcel还支持将Excel文件转换为PDF文件,这一功能底层依赖于Apache POIitext-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的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值