EasyExcel入门使用

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。他能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。
EasyExcel入门使用_第1张图片

EasyExcel 的主要特点如下:

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

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

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

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

EasyExcel官网

1.入门案例

1.在pom文件中加入依赖

<dependency>
    <groupId>com.alibabagroupId>
    <artifactId>easyexcelartifactId>
    <version>3.1.0version>
dependency>

2.定义一个实体类来封装每一行的数据

@Data
public class CategoryExcelVo {

	@ExcelProperty(value = "id" ,index = 0)
	private Long id;

	@ExcelProperty(value = "名称" ,index = 1)
	private String name;

	@ExcelProperty(value = "图片url" ,index = 2)
	private String imageUrl ;

	@ExcelProperty(value = "上级id" ,index = 3)
	private Long parentId;

	@ExcelProperty(value = "状态" ,index = 4)
	private Integer status;

	@ExcelProperty(value = "排序" ,index = 5)
	private Integer orderNum;
}

3.定义一个监听器,监听解析到的数据


public class ExcelListener<T> extends AnalysisEventListener<T> {


    private List<T> data = new ArrayList<>();


    /**
     * 读取excel内容 从第二行开始读取,把每行读取内容封装到t对象里
     * @param t
     * @param analysisContext
     */
    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        data.add(t);
    }


    public List<T> getData(){
        return data;
    }

    // excel解析完毕以后需要执行的代码
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

4.编写测试方法


public class EasyExcelTest {
    public static void main(String[] args) {
//        read();
        write();
    }



    //读操作
    public static void read(){
        // 定义读取excel文件为止
        String fileName = "E://资料//资料//01.xlsx";
        // 调用方法
        // 创建一个监听器对象
        ExcelListener<CategoryExcelVo> excelListener = new ExcelListener();
        // 解析excel表格
        EasyExcel.read(fileName, CategoryExcelVo.class,excelListener).sheet().doRead();
        List<CategoryExcelVo> data = excelListener.getData();

        System.out.println(data);
    }


    //读操作
    public static void write(){
        List<CategoryExcelVo> list = new ArrayList<>() ;
        list.add(new CategoryExcelVo(1L , "数码办公" , "",0L, 1, 1)) ;
        list.add(new CategoryExcelVo(11L , "华为手机" , "",1L, 1, 2)) ;
        EasyExcel.write("E://资料//资料//01.xlsx" , CategoryExcelVo.class).sheet("分类数据1").doWrite(list);
    }
}

2.导出功能

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

CategoryController

@GetMapping(value = "/exportData")
public void exportData(HttpServletResponse response) {
    categoryService.exportData(response);
}

CategoryService

    void exportData(HttpServletResponse response);

CategoryServiceImpl

@Override
    public void exportData(HttpServletResponse response) {

        try {
            // 设置响应头信息和其他信息
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");

            //URLEncoder.encode可以防止中文乱码
            String fileName = URLEncoder.encode("分类数据","UTF-8");

            // 设置响应头信息
            response.setHeader("Content-disposition","attachment;filename="+fileName+".xlsx");

            // 调用mapper方法查询所有分类,返回list集合
            List<Category> categoryList = categoryMapper.findAll();

            List<CategoryExcelVo> categoryExcelVoList = new ArrayList<>();

            for(Category category : categoryList){
                CategoryExcelVo categoryExcelVo = new CategoryExcelVo();
                BeanUtils.copyProperties(category,categoryExcelVo,CategoryExcelVo.class);
                categoryExcelVoList.add(categoryExcelVo);
            }

            // 调用EasyExcel的write方法完成写操作
            EasyExcel.write(response.getOutputStream(), CategoryExcelVo.class).sheet("分类数据").doWrite(categoryExcelVoList);
        } catch (Exception e){
            e.printStackTrace();
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }

    }

CategoryMapper

  List<Category> findAll();

CategoryMapper

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

category.js

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

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>

3.导入功能

用户选择要导入的excel文件,选择完毕以后将文件上传到服务端,服务端通过easyExcel解析文件的内容,然后将解析的结果存储到category表中。

CategoryController

    /**
     * 导入功能
     * @param file
     * @return
     */
    @PostMapping("/importData")
    public Result importData(MultipartFile file){
        categoryService.importData(file);
        return Result.build(null , ResultCodeEnum.SUCCESS) ;
    }

CategoryService

    void importData(MultipartFile file);

CategoryServiceImpl

   @Override
    public void importData(MultipartFile file) {

        try {

            //创建监听器对象,传递mapper对象
            ExcelListener<CategoryExcelVo> excelListener = new ExcelListener<>(categoryMapper);
            EasyExcel.read(file.getInputStream(),CategoryExcelVo.class,excelListener).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
            throw new GuiguException(ResultCodeEnum.DATA_ERROR);
        }
    }

CategoryMapper

    void batchInsert(List<CategoryExcelVo> categoryList);

CategoryMapper


    <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>

创建包listener,创建监听器类ExcelListener


/**
 * 监听器
 */
public class ExcelListener<T> implements ReadListener<T> {


    /**
     * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;


    /**
     * 缓存的数据
     */
    private List<T> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);


    //构造传递
    private CategoryMapper categoryMapper;

    public ExcelListener(CategoryMapper categoryMapper){
        this.categoryMapper = categoryMapper;
    }


    @Override
    public void invoke(T t, AnalysisContext analysisContext) {
        cachedDataList.add(t);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // excel解析完毕以后需要执行的代码
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
    }

    private void saveData() {
        categoryMapper.batchInsert((List<CategoryExcelVo>)cachedDataList);
    }
}

Category.vue

<div class="tools-div">
    <el-button type="success" size="small" @click="exportData">导出el-button>
    <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:8501/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>

你可能感兴趣的:(java,后端)