16 - component-excel组件的封装与集成

component-excel组件的封装

概述

所有的操作是基于 easyExcel的封装, easyExcel是一个很完美的框架了,我的封装只是为了更方便业务上的复用。其他文字上的描述话不多说。

官网

很详细的官网 :

https://easyexcel.opensource.alibaba.com/docs/current/quickstart/read
https://github.com/alibaba/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/util/TestFileUtil.java
https://gitee.com/easyexcel/easyexcel/blob/master/easyexcel-test/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java

结合业务我对easyExcel的思考

  1. 同一个项目中大部分的业务导出excel数据逻辑是一样的,只是因模块而异导致excel的表头上的差异。能否封装一个统一的方法供每个业务模块调用,根据每个模块的模板来实现其功能。利用函数式接口作为回调和泛型的特性封装了一个统一的工具类ExcelUtil,将通用的方法都封装在ExcelUtil中的每一个静态方法中,任务业务模块都调用它
  2. 如果数据需要导出的数据量很大:比如日志,我需要导出几百上千万条,全部加载进内存还不会OOM 在函数式接口中分页抓取数据库中的数据,然后通过easyEasy直接写进文件,每次只加载一定量的数据到内存然后直接写进文件(磁盘)
  3. 如果同步导数据,数据量很大的话接口很容易超时。针对数据量大的接口需要改为异步操作,可以采用MQ或者JOB的方式
  4. 数据量太大sql语句必定很慢. 通过函数式接口回调分页查询数据
  5. 相同的数据要放到一起,相同类型的数据要在表格中集中显示:比如我导出商品(袜子,衣服,裤子),不能是一条袜子的数据,一条裤子的数据,再一条袜子的数据,存在数据穿插。可以通过数据库中的标识字段排序,或者将抓取来的数据在代码中通过Lambda做分类处理 .....
  6. 如果走异步,如何通知用户导出结果 ?如果需要很强的及时性可以通过websocket将完成信号告知前端,如果不需要很强的及时性可以让用户手动去刷新下载列表来查看导出的任务状态。
  7. 如果excel文件太大,目标用户打不开怎么办?设置导出的数据规则,超过一定数据切换sheet,超过定的sheet切换excel文件,避免一个excel文件写入过大的数据,假设一个excel写入几十个G的数据,用户的个人pc内存只有8G,那确实打开会有问题的

解决的思路

使用MQ(单体应用也可以使用本地的队列)

在这里插入图片描述

基于 JOB

在这里插入图片描述

组件类包结构简述

│ common-excel
│ pom.xml
└─fangsheng
      └─technology
          └─excel
              │  ExcelUtil.java  // 通用的工具类UploadDataListener.java // excel文件上传数据解析监听类 
              │
              └─template // 模板(表头)包,当前包下放置各种下载模板的DTO
                      HeroExcelDTO.java // 导出LOL英雄联盟中的英雄的 template

核心代码类

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.fangsheng.technology</groupId>
        <artifactId>common-component</artifactId>
        <version>1.0.0-SNAPSHOT</version>
    </parent>

    <artifactId>common-excel</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <description>
        Excel util
    </description>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <easyexcel.version>3.0.5</easyexcel.version>
    </properties>

    <dependencies>
        <!-- 使用哪个lib,引入哪个lib, version 由 parent 定义,便于管理,也可以覆盖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>${easyexcel.version}</version>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.2.2.RELEASE</version>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.fangsheng.technology</groupId>
            <artifactId>common-util</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fangsheng.technology</groupId>
            <artifactId>common-dto</artifactId>
        </dependency>

    </dependencies>

</project>

HeroExcelDTO.java

package com.fangsheng.technology.excel.template;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.*;

import java.util.Date;

/**
 * @author lht
 * @date 2023/12/7 16:55:09
 */
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@Builder
@ToString
public class HeroExcelDTO {

    @ExcelProperty("英雄名字")
    @ColumnWidth(20)
    private String heroName;

    @ColumnWidth(10)
    @ExcelProperty("英雄年龄")
    private Integer heroAge;

    @ColumnWidth(40)
    @ExcelProperty("英雄技能")
    private String heroSkill;

    @ColumnWidth(20)
    @ExcelProperty("加入联盟的日期")
    private Date date;


    public String getHeroName() {
        return heroName;
    }

    public void setHeroName(String heroName) {
        this.heroName = heroName;
    }

    public Integer getHeroAge() {
        return heroAge;
    }

    public void setHeroAge(Integer heroAge) {
        this.heroAge = heroAge;
    }

    public String getHeroSkill() {
        return heroSkill;
    }

    public void setHeroSkill(String heroSkill) {
        this.heroSkill = heroSkill;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

ExcelUtil.java

package com.fangsheng.technology.excel;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.fangsheng.technology.dto.PageQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;

/**
 * @author lht
 * @date 2023/12/7 16:00:14
 */
@Slf4j
public class ExcelUtil {

    /**
     * 当前下载会把所有的数据写到同一个表格的同一个sheet中,会将文件直接写到页面
     * 1. excel对一个sheet存放的最大数据量,是有做限制的.
     * 2. 一个sheet最多可以保存1048576行数据。否则在保存数据时会直接报错
     * 文件下载(失败了会返回一个有部分数据的Excel)
     *
     * @param response 响应对象
     * @param clz      head的Class类型
     * @param data     写入表格的数据
     * @param fileName 文件名
     *                 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
     */
    public static <D> void downLoad(HttpServletResponse response, Class<D> clz, List<D> data, String fileName) throws IOException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        EasyExcel.write(response.getOutputStream(), clz).sheet(fileName).registerWriteHandler(new HorizontalCellStyleStrategy()).doWrite(data);

    }


    /**
     * 当前下载会把所有的数据写到同一个表格的不同sheet中 会将文件直接写到页面
     * <p>
     * 文件下载(失败了会返回一个有部分数据的Excel)
     *
     * @param response 响应对象
     * @param clz      head的Class类型
     * @param data     写入表格的数据,list.size() 就是sheet的数量
     * @param fileName 文件名
     */
    public static <D> void downLoadMultiSheet(HttpServletResponse response, Class<D> clz, List<List<D>> data, String fileName) throws IOException {
        if (CollectionUtil.isEmpty(data)) {
            log.error("===============> 需要导出的数据为空 <===============");
            return;
        }
        fileName = setCommonResponse(response, fileName);
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), clz).build();
        Integer sheetNo = 0;
        for (List<D> ds : data) {
            if (CollectionUtil.isEmpty(ds)) {
                continue;
            }
            ++sheetNo;
            // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
            WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, fileName + sheetNo)
                    .registerWriteHandler(new HorizontalCellStyleStrategy())
                    .build();
            excelWriter.write(ds, writeSheet);
        }
        excelWriter.finish();
        log.info("===============> downLoadMultiSheet finished <===============");
    }


    /**
     * 当前下载会把所有的数据写到同一个表格的不同sheet中,并将excel写入到文件中
     * 文件默认位置放在项目Resource下: /D:/fangsheng/code/study/api-server/api-server-start/target/classes/英雄-20231208165825.xlsx
     *
     * @param fileName      需要指定excel文件的文件名
     * @param clz           head的Class类型
     * @param dataFn        每次抓取数据的 Function()
     * @param sheetCapacity 单个sheet的限定容量
     * @param pageQuery 查询对象
     */
    public static <D> String downLoadMultiSheetToFile(String fileName, Class<D> clz, Function<PageQuery, List<D>> dataFn, Integer sheetCapacity,PageQuery pageQuery) throws IOException {

        //当前 sheet 已经存入的容量
        int currentSheetCapacity = 0;
        //当前 sheet 号
        int sheetNo = 1;
        //每次抓取的数据
        List<D> grabData = null;
        //在当前sheet号写入的数据
        List<D> cData = null;
        //在下一个sheet号写入的数据
        List<D> nData = null;
        String file = getFile(fileName);
        ExcelWriter excelWriter = EasyExcel.write(file, clz).build();
        //初始化第一个 WriteSheet, 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
        WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, fileName + "-" + sheetNo)
                .registerWriteHandler(new HorizontalCellStyleStrategy())
                .build();
        while (CollectionUtil.isNotEmpty(grabData = dataFn.apply(pageQuery))) {
            if (grabData.size() > sheetCapacity) {
                // 每次查询的数据不能大于单个sheet的容量
                throw new RuntimeException("单次待插入sheet的数据不能大于sheet的容量");
            }

            if (grabData.size() + currentSheetCapacity >= sheetCapacity) {
                int overNo = grabData.size() + currentSheetCapacity - sheetCapacity;
                // 将 d 拆分成两张sheet的data
                cData = grabData.subList(0, overNo);
                nData = grabData.subList(overNo, grabData.size());
                currentSheetCapacity = nData.size();
            } else {
                cData = grabData;
                currentSheetCapacity += cData.size();
            }
            //先加入当前sheet号的数据 (直接将每次分页查询从数据库加载来的数据写进文件中(磁盘),避免大量的数据加载到内存OOM)
            excelWriter.write(cData, writeSheet);
            if (CollectionUtil.isNotEmpty(nData)) {
                sheetNo++;
                writeSheet = EasyExcel.writerSheet(sheetNo, fileName + "-" + sheetNo)
                        .registerWriteHandler(new HorizontalCellStyleStrategy())
                        .build();
                //写入下一个sheet号的数据
                excelWriter.write(nData, writeSheet);
                nData = null;
            }
            int pageIndex = pageQuery.getPageInfo().getPageIndex();
            pageQuery.getPageInfo().setPageIndex(pageIndex+1);
        }
        excelWriter.finish();
        log.info("===============> downLoadMultiSheetToFile finished <===============");
        return file;
    }

    private static String getFile(String fileName) {
        String customName = "";
        if (StringUtils.isNotBlank(fileName)) {
            customName = fileName;
        }
        String currentDate = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
        String file = Objects.requireNonNull(ExcelUtil.class.getResource("/")).getPath() + customName + "-" + currentDate + ".xlsx";
        log.info("===============> 生成的文件为: {} <===============", file);
        return file;

    }


    /**
     * easyExcel 支持的表格后缀 (ExcelTypeEnum枚举中有定义)
     */
    public static final String FILE_NAME_SUFFIX = ".*\\.(xls|xlsx|csv)$";

    /**
     * 文件上传
     * <p>
     * 1. 创建excel对应的实体对象
     * <p>
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器
     * <p>
     * 3. 直接读即可
     */
    public static <D> String upload(MultipartFile file, Class<D> clz, Consumer<List<D>> consumer) throws IOException {
        // TODO 将判断逻辑抽取成Assert,魔法值封装成常量或者从配置文件读取,异常类定义成业务异常
        //获取要上传文件的名称
        String fileName = file.getOriginalFilename();
        //如果名称为空,返回一个文件名为空的错误
        if (StringUtils.isBlank(fileName)) {
            throw new RuntimeException("文件名不能为空!");
        }
        //如果文件超过最大值,返回超出可上传最大值 (文件不能超过2G)
        if (file.isEmpty() || file.getSize() > 20_0000_0000L) {
            throw new RuntimeException("文件大小不符合规范!");
        }
        //校验后缀名 FILE_NAME_SUFFIX
        if (!fileName.matches(FILE_NAME_SUFFIX)) {
            throw new RuntimeException("文件后缀名不符合规范!");
        }

        EasyExcel.read(file.getInputStream(), clz, new UploadDataListener(consumer)).sheet().doRead();
        return "success";
    }


    public static String setCommonResponse(HttpServletResponse response, String fileName) throws UnsupportedEncodingException {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        return fileName;
    }


}

UploadDataListener.java


package com.fangsheng.technology.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.function.Consumer;

/**
 * @author lht
 * @date 2023/12/7 16:34:24
 */
@Slf4j
public class UploadDataListener<D> implements ReadListener<D> {

    private Consumer<List<D>> consumer;


    /**
     * 每隔100条存储数据库,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    private List<D> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    public UploadDataListener(Consumer<List<D>> consumer) {
        this.consumer = consumer;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data one row value. It is same as {@link AnalysisContext#readRowHolder()}
     * @param context 上下文
     */
    @Override
    public void invoke(D data, AnalysisContext context) {
        log.info("解析到一条数据:{}", JSON.toJSONString(data));
        cachedDataList.add(data);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context 上下文
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据,开始存储数据库!", cachedDataList.size());
        consumer.accept(cachedDataList);
        log.info("存储数据库成功!");
    }

    public Consumer<List<D>> getConsumer() {
        return consumer;
    }

    public void setConsumer(Consumer<List<D>> consumer) {
        this.consumer = consumer;
    }
}

component-excel组件的集成测试

测试代码集成在api-server,文件上传,数据直接导出到页面,数据异步导出到文件测试

测试核心代码

pom

            <dependency>
                <groupId>com.fangsheng.technology</groupId>
                <artifactId>common-excel</artifactId>
                <version>${component-excel.version}</version>
            </dependency>

在component-excel组件中定义模板

package com.fangsheng.technology.excel.template;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import lombok.*;

import java.util.Date;

/**
 * @author lht
 * @date 2023/12/7 16:55:09
 */
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@Builder
@ToString
public class HeroExcelDTO {

    @ExcelProperty("英雄名字")
    @ColumnWidth(20)
    private String heroName;

    @ColumnWidth(10)
    @ExcelProperty("英雄年龄")
    private Integer heroAge;

    @ColumnWidth(40)
    @ExcelProperty("英雄技能")
    private String heroSkill;

    @ColumnWidth(20)
    @ExcelProperty("加入联盟的日期")
    private Date date;


    public String getHeroName() {
        return heroName;
    }

    public void setHeroName(String heroName) {
        this.heroName = heroName;
    }

    public Integer getHeroAge() {
        return heroAge;
    }

    public void setHeroAge(Integer heroAge) {
        this.heroAge = heroAge;
    }

    public String getHeroSkill() {
        return heroSkill;
    }

    public void setHeroSkill(String heroSkill) {
        this.heroSkill = heroSkill;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}

定义查询Search对象

package com.fangsheng.technology.facade.hero.cmd.query;

import com.fangsheng.technology.dto.PageQuery;
import lombok.Data;

/**
 * @author lht
 * @date 2023/12/9 10:06:54
 */
@Data
public class HeroExcelQry extends PageQuery {

    private String heroName;



}

实现函数式回调接口

package com.fangsheng.technology.app.hero.consumer;

import cn.hutool.core.collection.CollectionUtil;
import com.fangsheng.technology.domain.hero.gateway.HeroGateway;
import com.fangsheng.technology.excel.template.HeroExcelDTO;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;
import java.util.function.Consumer;

/**
 * @author lht
 * @date 2023/12/7 17:39:14
 * 英雄文件上传消费者函数式接口
 */
@Component
public class HeroExcelConsumer implements Consumer<List<HeroExcelDTO>> {

    //@Resource
    //private HeroGateway heroGateway;

    @Override
    public void accept(List<HeroExcelDTO> heroes) {
        if (CollectionUtil.isEmpty(heroes)){
            return;
        }
        // 假设有一个保存到数据库的方法,会调HeroMapper保存
        //heroGateway.addBatch(heroes);

        heroes.forEach(System.out::println);

    }



}


package com.fangsheng.technology.app.hero.consumer;

import com.fangsheng.technology.dto.PageQuery;
import com.fangsheng.technology.excel.template.HeroExcelDTO;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.function.Function;

/**
 * @author lht
 * @date 2023/12/8 15:21:01
 * 英雄数据导出的函数式接口实现
 */
@Component
public class HeroExcelFunction implements Function<PageQuery, List<HeroExcelDTO>> {


    //@Resource
    //private HeroGateway heroGateway;

    /**
     * 提供了一个方法,供ExcelUtil中调用,通过此方法不停地分页查询数据。
     *
     * @Param integer 当前页
     */
    @Override
    public List<HeroExcelDTO> apply(PageQuery pageQuery) {
        // 假设有一个通过数据库分页条件去查询数据库数据的方法。
        //heroGateway.queryHeroBySearch(分页查询条件对象);
        //将查来的数据聚合处理后再返回出去(比如:我希望导出的表格是辅助类英雄,坦克型英雄,射手型英雄 ... 都是连续一起的方式呈现)

        return mockDate(pageQuery.getPageInfo().getPageIndex());

    }

    /**
     * 先写死测试数据,默认只有3页数据,第4条返回空集合,每次请求给4条数据
     */
    private List<HeroExcelDTO> mockDate(Integer integer) {
        List<HeroExcelDTO> res = new ArrayList<>();
        if (integer == 4) {
            return res;
        }

        for (int i = 0; i < 4; i++) {
            HeroExcelDTO olaf = HeroExcelDTO.builder()
                    .heroName("奥拉夫" + integer + "-" + i)
                    .heroAge(1000)
                    .heroSkill("狂战之怒,鲁莽挥击,诸神黄昏 ,狂战士之怒" + integer + "-" + i)
                    .date(new Date()).build();
            res.add(olaf);
        }
        return res;

    }


}

controller

package com.fangsheng.technology.adapter;

import com.fangsheng.technology.facade.hero.cmd.HeroReq;
import com.fangsheng.technology.facade.hero.cmd.query.HeroExcelQry;
import com.fangsheng.technology.facade.hero.resp.HeroResp;
import com.fangsheng.technology.facade.hero.web.HeroFacade;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;

/**
 * @author lht
 * @date 2023/11/1 14:50:38
 * 数据库连接测试控制器
 */
@Slf4j
@RestController
@RequestMapping("/hero")
public class HeroController {


    @Resource
    private HeroFacade heroFacade;


  
    @GetMapping("download")
    public void downLoadHeroExcel(HttpServletResponse response){
        heroFacade.downLoadHeroExcel(response);
    }

    @PostMapping("upload")
    public String uploadHeroExcel(MultipartFile file){
      return heroFacade.uploadHeroExcel(file);
    }

    /**
     * @param heroExcelQry 筛选条件对象
     */
    @PostMapping("download/file")
    public Boolean downLoadMultiSheetToFile(@RequestBody HeroExcelQry heroExcelQry){
        return heroFacade.downLoadMultiSheetToFile(heroExcelQry);
    }


}


调用逻辑代码

少量数据直接导出到页面
package com.fangsheng.technology.app.hero.executor;

import com.fangsheng.technology.excel.ExcelUtil;
import com.fangsheng.technology.excel.template.HeroExcelDTO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.List;

/**
 * @author lht
 * @date 2023/12/7 17:19:43
 */
@Slf4j
@Component
public class HeroDownLoadExcelExe {


    public void execute(HttpServletResponse response){

        // 1. 假设从数据库中下载了数据
        HeroExcelDTO womanPolice = HeroExcelDTO.builder()
                .heroName("皮城女警")
                .heroAge(1000)
                .heroSkill("爆头,夹子,神网束缚,让子弹飞")
                .date(new Date()).build();

        HeroExcelDTO olaf = HeroExcelDTO.builder()
            .heroName("奥拉夫")
            .heroAge(1000)
            .heroSkill("狂战之怒,鲁莽挥击,诸神黄昏 ,狂战士之怒")
            .date(new Date()).build();

        try {
            ExcelUtil.downLoad(response,HeroExcelDTO.class, List.of(womanPolice,olaf),"英雄");
        } catch (IOException e) {
            e.printStackTrace();
            log.error("excel 下载异常");
        }


    }
}

文件上传
package com.fangsheng.technology.app.hero.executor;

import com.fangsheng.technology.app.hero.consumer.HeroExcelConsumer;
import com.fangsheng.technology.excel.ExcelUtil;
import com.fangsheng.technology.excel.template.HeroExcelDTO;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * @author lht
 * @date 2023/12/7 17:35:34
 */
@Component
public class HeroUploadExcelExe {

    @Resource
    private HeroExcelConsumer heroExcelConsumer;

    public String execute(MultipartFile file) {

        String upload = "upload fail";
        try {
            upload = ExcelUtil.upload(file, HeroExcelDTO.class, heroExcelConsumer);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return upload;
    }
}

异步导出大量的数据到文件
package com.fangsheng.technology.app.hero.executor;

import com.fangsheng.technology.app.hero.consumer.HeroExcelFunction;
import com.fangsheng.technology.excel.ExcelUtil;
import com.fangsheng.technology.excel.template.HeroExcelDTO;
import com.fangsheng.technology.facade.hero.cmd.query.HeroExcelQry;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;

/**
 * @author lht
 * @date 2023/12/8 15:19:29
 */
@Component
public class HeroDownLoadMultiSheetToFileExe {

    @Resource
    private HeroExcelFunction heroExcelFunction;


    public Boolean execute(HeroExcelQry heroExcelQry) {

        // TODO 需要设计一张下载记录,表往表中插入一条记录。当然下载的操作需要放到异步线程去做的。

        String file = null;
        try {
            file = ExcelUtil.downLoadMultiSheetToFile("英雄", HeroExcelDTO.class, heroExcelFunction, 10,heroExcelQry);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //如果下载成功 file = /D:/fangsheng/code/study/api-server/api-server-start/target/classes/英雄-20231208165825.xlsx
        // TODO 获取到文件后,将其上传到 OSS 文件服务器上即可,将记录标识为下载完成,然后websocket将异步通知前端去下载
        return StringUtils.isNotBlank(file);
    }

}

测试的结果

在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值