java项目导入导出Excel表格-Esay-Excel

发布于:2025-02-10 ⋅ 阅读:(8) ⋅ 点赞:(0)

数据导入导出意义

后台管理系统是管理、处理企业业务数据的重要工具,在这样的系统中,数据的导入和导出功能是非常重要的,其主要意义包括以下几个方面:

1、提高数据操作效率:手动逐条添加或修改数据不仅费时费力,而且容易出错,此时就可以将大量数据从Excel等表格软件中导入到系统中时,通过数据导入功能,可以直接将表格中的数

据批量导入到系统中,提高了数据操作的效率。

2、实现数据备份与迁移:通过数据导出功能,管理员可以将系统中的数据导出为 Excel 或其他格式的文件,以实现数据备份,避免数据丢失。同时,也可以将导出的数据文件用于数据迁

移或其他用途。

3、方便企业内部协作:不同部门可能会使用不同的系统或工具进行数据处理,在这种情况下,通过数据导入和导出功能,可以方便地转换和共享数据,促进企业内部协作。

2.2 EasyExcel简介

官网地址:EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网

EasyExcel 的主要特点如下:

1、高性能:EasyExcel 采用了异步导入导出的方式,并且底层使用 NIO 技术实现,使得其在导入导出大数据量时的性能非常高效。

2、易于使用:EasyExcel 提供了简单易用的 API,用户可以通过少量的代码即可实现复杂的 Excel 导入导出操作。

3、增强的功能“EasyExcel 支持多种格式的 Excel 文件导入导出,同时还提供了诸如合并单元格、数据校验、自定义样式等增强的功能。

4、可扩展性好:EasyExcel 具有良好的扩展性,用户可以通过自定义 Converter 对自定义类型进行转换,或者通过继承 EasyExcelListener 来自定义监听器实现更加灵活的需求。

2.3 入门案例demo

2.3.1 解析Excel数据

需求:对资料中的excel数据进行解析,将其存储到对应的List集合中,并遍历List集合

步骤:

1、在spzx-model的pom.xml文件中添加如下依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.0</version>
</dependency>

2、定义一个实体类来封装每一行的数据,如下所示:

package com.atguigu.spzx.manager.test;

import lombok.Data;

/**
 * @version: java version 1.8
 * @Author: Mr Orange
 * @description:
 * @date: 2025-02-08 15:15
 */
@Data
public class Student {
    private String name;
    private int age;
}

3、定义一个监听器,监听解析到的数据,如下所示:

package com.atguigu.spzx.manager.test;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import java.util.ArrayList;
import java.util.List;
/**
 * @version: java version 1.8
 * @Author: Mr Orange
 * @description: 这是一个Excel读取监听器,用于逐行读取学生数据,并在达到一定数量后进行处理(例如,批量插入数据库)。
 * @date: 2025-02-08 15:33
 */
// 该类继承自 AnalysisEventListener,泛型为 Student,
// 意味着该监听器关注的是 Student 类型的数据。
public class StudentReadListener extends AnalysisEventListener<Student> {
    // 为了存储读取的学生数据,使用 List 集合。
    List<Student> list = new ArrayList<>();
    // 每读取一行数据时,该方法会被调用。
    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        // 将读取的 Student 对象添加到 list 中。
        list.add(student);
        // 判断当前 list 中的数据量是否超过 100 条。
        // 如果超过,则进行处理(例如,批量插入到数据库中)。
        if (list.size() > 100) {
            // 数据处理的模拟输出,可以根据实际需要进行数据库操作。
            System.out.println("当前读取的学生数量达到 100,准备进行数据库插入。");
            System.out.println(list); // 输出当前读取的学生数据。
            System.out.println("开始插入数据库...");
            // 在这里添加你的数据库插入逻辑。
            // 清空 list,以便接收后续的数据。
            list.clear();
        }
    }
    // 该方法在所有数据读取完后调用,用于处理剩余的数据。
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 输出所有已读取的数据。
        System.out.println("所有行都读取完毕");
        System.out.println(list); // 输出剩余未插入数据库的学生数据。
        // 如果还有未处理的数据,进行处理操作(例如,插入数据库)。
        if (!list.isEmpty()) {
            System.out.println("处理剩余的数据...");
            // 在这里添加你的数据库插入逻辑。
            list.clear(); // 清空 list。
        }
    }
}

4、编写测试方法

// 声明使用的包
package com.atguigu.spzx.manager.test;
// 导入EasyExcel库以便处理Excel文件
import com.alibaba.excel.EasyExcel;
// 导入ArrayList和List类
import java.util.ArrayList;
import java.util.List;
/**
 * @version: java version 1.8
 * @Author: Mr Orange
 * @description: 该类用于生成一个包含学生信息的Excel文件
 * @date: 2025-02-08 15:15
 */
public class Test {
    public static void main(String[] args) {
        // 创建一个学生对象列表
        List<Student> studentList = new ArrayList<>();
        // 使用循环生成10个学生对象并添加到列表中
        for (int i = 0; i < 10; i++) {
            Student student = new Student(); // 创建一个新的学生对象
            student.setAge(i); // 设置学生年龄为当前索引值i
            student.setName("张三" + i); // 设置学生姓名为"张三"加上当前索引值i
            studentList.add(student); // 将学生对象添加到列表中
        }
        // 使用EasyExcel库写入Excel文件
        EasyExcel.write("D:\\student.xlsx", Student.class) // 指定输出文件路径及数据类型
                  .sheet("学生信息") // 指定Excel表单名称
                  .doWrite(studentList); // 将学生列表写入Excel文件
    }
}

2.3.2 存储数据到Excel

需求:将如下的集合数据存储到Excel中文件中

List<CategoryExcelVo> list = new ArrayList<>() ;
list.add(new CategoryExcelVo(1L , 0L , "数码办公" , 1 , 1)) ;
list.add(new CategoryExcelVo(2L , 1L , "手机通讯" , 1 , 1)) ;
list.add(new CategoryExcelVo(3L , 2L , "手机" , 1 , 0)) ;

代码实现:

@Test
public void saveDataToExcel() {
    List<CategoryExcelVo> list = new ArrayList<>() ;
    list.add(new CategoryExcelVo(1L , 0L , "数码办公" , 1 , 1)) ;
    list.add(new CategoryExcelVo(2L , 1L , "手机通讯" , 1 , 1)) ;
    list.add(new CategoryExcelVo(3L , 2L , "手机" , 1 , 0)) ;
    EasyExcel.write("D://分类数据.xlsx" , CategoryExcelVo.class).sheet("分类数据").doWrite(list);
}

2.4 导出功能

2.4.1 需求说明

当用户点击导出按钮的时候,此时将数据库中的所有的分类的数据导出到一个excel文件中,如下所示:

2.4.2 后端接口

CategoryController

表现层代码实现:

    @Operation(summary = "导出文件")
    @GetMapping(value = "/exportData")
    public void exportData(HttpServletResponse response) {
        categoryService.exportData(response);
    }
CategoryService

业务层代码实现:

@SneakyThrows // 自动处理异常,避免手动捕获和处理
@Override // 表示重写父类或接口的方法
public void exportData(HttpServletResponse response) {
    // 创建一个列表用于存放要导出的CategoryExcelVo对象
    List<CategoryExcelVo> categoryExcelVos = new ArrayList<>();
    // 从数据库中查询所有的Category对象
    List<Category> categories = categoryMapper.selectAll();
    // 将从数据库中查询到的Category对象转换成CategoryExcelVo对象
    for (Category category : categories) {
        // 创建一个新的CategoryExcelVo对象
        CategoryExcelVo categoryExcelVo = new CategoryExcelVo();
        // 复制Category对象的属性到CategoryExcelVo对象中
        // 第三个参数用来指定忽略属性(这里是CategoryExcelVo.class,通常是传递到这个参数的需要忽略的属性,文中的使用可能不太标准,需按实际情况修改)
        BeanUtils.copyProperties(category, categoryExcelVo, CategoryExcelVo.class);
        // 将转换后的CategoryExcelVo对象添加到列表中
        categoryExcelVos.add(categoryExcelVo);
    }
    // 使用EasyExcel将数据写入HTTP响应的输出流中
    // 设置导出的Excel表格标题为"分类数据"
    EasyExcel.write(response.getOutputStream(), CategoryExcelVo.class)
            .sheet("分类数据") // 设置工作表名称
            .doWrite(categoryExcelVos); // 执行写入操作,将数据写入Excel文件
}
CategoryMapper

持久层代码实现:

public interface CategoryMapper {

    List<Category> selectAll();
}
CategoryMapper.xml

在映射文件中添加如下sql语句:

<select id="selectAll" resultMap="categoryMap">
    select <include refid="columns" />
    from category
    where is_deleted = 0
    order by id
</select>

2.4.3 前端对接

category.js

在src/api文件夹下创建一个category.js文件,文件的内容如下所示:

// 导出方法
export const ExportCategoryData = () => {
  return request({
    url: `${api_name}/exportData`,
    method: 'get',
    responseType: 'blob'  // // 这里指定响应类型为blob类型,二进制数据类型,用于表示大量的二进制数据
  })
}
category.vue

修改category.vue文件,内容如下所示:

<div class="tools-div">
    <el-button type="success" size="small" @click="exportData">导出</el-button>
</div>
​
<script setup>
import { ExportCategoryData } from '@/api/category.js'
​
const exportData = () => {
  // 调用 ExportCategoryData() 方法获取导出数据
  ExportCategoryData().then(res => {
      // 创建 Blob 对象,用于包含二进制数据
      const blob = new Blob([res]);             
      // 创建 a 标签元素,并将 Blob 对象转换成 URL
      const link = document.createElement('a'); 
      link.href = window.URL.createObjectURL(blob);
      // 设置下载文件的名称
      link.download = '分类数据.xlsx';
      // 模拟点击下载链接
      link.click();
  })  
}
</script>

2.5 导入功能

2.5.1 需求说明

当用户点击导入按钮的时候,此时会弹出一个对话框,让用户选择要导入的excel文件,选择完毕以后将文件上传到服务端,服务端通过easyExcel解

析文件的内容,然后将解析的结果存储到category表中。如下所示:

import-category

2.5.2 后端接口

CategoryController

表现层代码实现:

    @Operation(summary = "导入文件")
    @PostMapping("importData")
    public Result importData(MultipartFile file) {
        categoryService.importData(file);
        return Result.ok(null);
    }
CategoryService

业务层代码实现:

package com.atguigu.spzx.manager.service.imp;
import com.alibaba.excel.EasyExcel;
import com.atguigu.spzx.manager.listener.CategoryReadListener;
import com.atguigu.spzx.manager.mapper.CategoryMapper;
import com.atguigu.spzx.manager.service.CategoryService;
import com.atguigu.spzx.model.entity.product.Category;
import com.atguigu.spzx.model.vo.product.CategoryExcelVo;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
 * @version: java version 1.8
 * @Author: Mr Orange
 * @description: CategoryServiceImpl是CategoryService接口的实现类,用于处理分类数据的导入。
 * @date: 2025-02-08 10:48
 */
@Service // 该注解标记此类为服务层组件,Spring会自动扫描并注册此类为Bean
public class CategoryServiceImpl implements CategoryService {
    @Autowired // 自动注入CategoryMapper,用于数据库操作
    CategoryMapper categoryMapper;
    /**
     * 导入分类数据的方法
     * @param multipartFile 上传的Excel文件
     */
    @Override
    @SneakyThrows // 该注解用于隐藏检查异常,简化代码
    public void importData(MultipartFile multipartFile) {
        // 获取上传文件的输入流
        InputStream inputStream = multipartFile.getInputStream();
        // 使用EasyExcel库读取Excel文件,指定模板类为CategoryExcelVo和自定义监听器CategoryReadListener
        EasyExcel.read(inputStream, CategoryExcelVo.class, new CategoryReadListener(categoryMapper))
                 .sheet("分类数据") // 指定Excel工作表名称为“分类数据”
                 .doRead(); // 执行读取操作
    }
}
CategoryReadListener (自定义监听器)
package com.atguigu.spzx.manager.listener;
// 导入所需的类
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.atguigu.spzx.manager.mapper.CategoryMapper;
import com.atguigu.spzx.model.vo.product.CategoryExcelVo;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
/**
 * @version: java version 1.8
 * @Author: Mr Orange
 * @description: 此类是用于处理 Excel 文件中的分类数据的监听器
 * @date: 2025-02-08 15:59
 */
@NoArgsConstructor
public class CategoryReadListener extends AnalysisEventListener<CategoryExcelVo> {
    // 存储读取的分类数据的列表
    List<CategoryExcelVo> list = new ArrayList<>();
    // 注入的CategoryMapper用于数据库操作
    @Autowired
    CategoryMapper categoryMapper;
    // 构造函数,接收一个CategoryMapper实例
    public CategoryReadListener(CategoryMapper categoryMapper) {
        this.categoryMapper = categoryMapper;
    }
    // 处理每一行的解析数据
    @Override
    public void invoke(CategoryExcelVo categoryExcelVo, AnalysisContext analysisContext) {
        // 将解析得到的分类数据添加到列表
        list.add(categoryExcelVo);
        // 每当列表的大小达到100条时进行批量插入
        if (list.size() >= 100) {
            System.out.println("批处理,每100条数据插入一次数据库");
            System.out.println(list);
            // 调用mapper的批量插入方法,将数据插入数据库
            categoryMapper.batchInsert(list);
            // 清空当前列表以准备下次的插入
            list.clear();
        }
    }
    // 所有数据解析完成后调用
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        System.out.println("全部数据读取完毕");
        // 打印最后未插入的数据
        System.out.println(list);
        // 插入剩余的数据
        categoryMapper.batchInsert(list);
        // 清空列表
        list.clear();
    }
}
CategoryMapper

持久层代码实现:

public interface CategoryMapper {
   
    void batchInsert(List<CategoryExcelVo> categoryExcelVos);
   
}
CategoryMapper.xml

映射文件中添加如下sql语句:

<insert id="batchInsert" useGeneratedKeys="true" keyProperty="id">
    insert into category (
        id,
        name,
        image_url,
        parent_id,
        status,
        order_num,
        create_time ,
        update_time ,
        is_deleted
    ) values
    <foreach collection="categoryList" item="item" separator="," >
        (
        #{item.id},
        #{item.name},
        #{item.imageUrl},
        #{item.parentId},
        #{item.status},
        #{item.orderNum},
        now(),
        now(),
        0
        )
    </foreach>
</insert>

2.5.3 前端对接

修改category.vue文件,内容如下所示:

<div class="tools-div">
    <el-button type="primary" size="small" @click="importData">导入</el-button>
</div>
​
<el-dialog v-model="dialogImportVisible" title="导入" width="30%">
    <el-form label-width="120px">
        <el-form-item label="分类文件">
            <el-upload
                       class="upload-demo"
                       action="http://localhost:8503/admin/product/category/importData"
                       :on-success="onUploadSuccess"
                       :headers="headers"
                       >
                <el-button type="primary">上传</el-button>
            </el-upload>
        </el-form-item>
    </el-form>
</el-dialog>
​
<script setup>
import { useApp } from '@/pinia/modules/app'
    
// 文件上传相关变量以及方法定义
const dialogImportVisible = ref(false)
const headers = {
  token: useApp().authorization.token     // 从pinia中获取token,在进行文件上传的时候将token设置到请求头中
}
const importData = () => {
  dialogImportVisible.value = true
}
​
// 上传文件成功以后要执行方法
const onUploadSuccess = async (response, file) => {
  ElMessage.success('操作成功')
  dialogImportVisible.value = false
  const { data } = await FindCategoryByParentId(0)
  list.value = data ; 
}
</script>

网站公告

今日签到

点亮在社区的每一天
去签到