EasyExcel导入、导出Excel

一、引入 jar 包

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

EasyExcel支持对实体类中的属性进行注解,方便后续进行读写操作。
id 为 表头,index 代表的是 第0行

@ExcelProperty(value="id",index=0)

二、编写excel导入对应实体类

@Data
public class ImportTradeDetailExcelMode implements Serializable {

    @ApiModelProperty(value = "贝壳楼盘名")
    @ExcelProperty("贝壳楼盘名")
    private String bkGardenName;

    @ApiModelProperty(value = "贝壳小区ID")
    @ExcelProperty("贝壳小区ID")
    private String bkGardenId;

    @ApiModelProperty(value = "城市区域")
    @ExcelProperty("区域")
    private String regionName;

    @ApiModelProperty(value = "城市名称")
    @ExcelProperty("城市")
    private String cityName;

    @ApiModelProperty(value = "成交日期")
    @ExcelProperty("成交时间")
    @DateTimeFormat("yyyy-MM-dd")
    private String tradeDate;

    @ApiModelProperty(value = "房源价格")
    @ExcelProperty("房源价格")
    private String roomPrice;

    @ApiModelProperty(value = "楼层")
    @ExcelProperty("楼层")
    private String floor;

    @ApiModelProperty(value = "房源面积(单位M2)")
    @ExcelProperty("房源面积")
    private BigDecimal roomArea;

    @ApiModelProperty(value = "朝向")
    @ExcelProperty("朝向")
    private String towards;

    @ApiModelProperty(value = "户型")
    @ExcelProperty("户型")
    private String roomType;

    @ApiModelProperty(value = "房屋用途")
    @ExcelProperty("房屋用途")
    private String roomUse;

    @ExcelProperty("错误消息")
    private String errorMsg;
}

三、导入

3.1、编写导入监听类

3.1.1 导入数据抽象数据监听类

/**
 * @author: huangyibo
 * @Date: 2022/3/24 16:03
 * @Description: 导入数据抽象类明细数据监听类
 */

@Slf4j
public abstract class ImportDataAbstractListener<T> extends AnalysisEventListener<T> {

    /**
     * 每隔3000条存储数据库, 然后清理successList ,方便内存回收
     */
    private static final int BATCH_COUNT = 3000;

    public static int getBatchCount() {
        return BATCH_COUNT;
    }

    /**
     * 获取导入结果
     * @return
     */
    public abstract ImportDataReturnVo<T> getResult();
}

3.1.2 编写具体监听类

/**
 * @author: huangyibo
 * @Date: 2022/3/24 16:03
 * @Description: 交易明细数据监听类
 */

@Slf4j
public class TradeDetailImportListener extends ImportDataAbstractListener<ImportTradeDetailExcelMode> {

    /**
     * 解析成功的数据
     */
    private List<DictImportTradeDetailDTO> successList = new ArrayList<>();

    /**
     * 格式有问题的数据
     */
    private List<ImportTradeDetailExcelMode> failList = new ArrayList<>();

    /**
     * 导入返回结果
     */
    private ImportDataReturnVo<ImportTradeDetailExcelMode> returnVo;

    private DictImportTradeDetailRemote dictImportTradeDetailRemote;

    private String userId;

    public TradeDetailImportListener(DictImportTradeDetailRemote dictImportTradeDetailRemote, String userId) {
        this.dictImportTradeDetailRemote = dictImportTradeDetailRemote;
        this.returnVo = new ImportDataReturnVo<>();
        this.returnVo.setImportId(IdUtils.getUuid());
        this.userId = userId;
    }

    @Override
    public void invoke(ImportTradeDetailExcelMode data, AnalysisContext analysisContext) {
        if("{}".equals(JSON.toJSONString(data))){
            //log.info("成交数据为空, 不做处理, data:{}", JSON.toJSONString(data));
            return;
        }
        DictImportTradeDetailDTO tradeDetailDTO = new DictImportTradeDetailDTO();
        BeanUtils.copyProperties(data, tradeDetailDTO);
        if (!validateEntity(data, tradeDetailDTO)) {
            log.info("该数据必传参数校验不通过, data:{}", JSON.toJSONString(data));
            return;
        }
        String tradeDate = data.getTradeDate();
        String newTread = tradeDate.replaceAll("\\.", "-");
        newTread = newTread.replaceAll("\\/", "-");
        Date date = Date.from(LocalDate.parse(newTread, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay(ZoneOffset.ofHours(8)).toInstant());
        tradeDetailDTO.setTradeDate(date);
        tradeDetailDTO.setImportId(returnVo.getImportId());
        tradeDetailDTO.setCreateId(userId);
        tradeDetailDTO.setModifyId(userId);
        successList.add(tradeDetailDTO);

        //每隔3000条存储数据库, 然后清理successList ,方便内存回收
        if (successList.size() >= getBatchCount()) {
            //分批存储数据
            saveData();
            successList.clear();
        }
    }

    /**
     * 校验表头,表头必须严格按照此格式和顺序
     * @param headMap
     * @param context
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        String[] headList = {"日期", "贝壳楼盘名", "寻渡小区ID", "贝壳小区ID", "区域", "商圈", "城市",
                "成交时间", "房源价格", "楼层", "房源面积", "朝向", "户型", "房屋用途", "房源ID"};
        Collection<String> values = headMap.values();
        for (String head : headList) {
            if (!values.contains(head)) {
                throw new RequestResultException("成交数据导入excel表头缺少[" + head + "]列");
            }
        }

        //表头顺序严格校验
        /*for (int i = 0; i < headList.length; i++) {
            if(!Objects.equals(headList[i], headMap.get(i))){
                throw new RequestResultException("成交数据导入excel表头["+headMap.get(i)+"]和解析格式excel表头["+headList[i]+"]不一致");
            }
        }*/
    }

    /**
     * 校验必传参数
     * @param data
     * @param tradeDetailDTO
     * @return
     */
    private boolean validateEntity(ImportTradeDetailExcelMode data, DictImportTradeDetailDTO tradeDetailDTO) {
        log.info("校验导入交易数据, data:{}", JSON.toJSONString(data));
        if(StringUtils.isEmpty(data.getBatchNum())){
            data.setErrorMsg("日期不能为空");
            failList.add(data);
            return false;
        }
        if(StringUtils.isEmpty(data.getKfGardenId())){
            data.setErrorMsg("看房网小区ID不能为空");
            failList.add(data);
            return false;
        }
        if(StringUtils.isEmpty(data.getRoomLink())){
            data.setErrorMsg("房源ID不能为空");
            failList.add(data);
            return false;
        }
        if(StringUtils.isEmpty(data.getRoomPrice())){
            data.setErrorMsg("房源价格不能为空");
            failList.add(data);
            return false;
        }else{
            try {
                BigDecimal roomPrice = new BigDecimal(String.valueOf(data.getRoomPrice()))
                        .multiply(new BigDecimal(10000));
                if(roomPrice.compareTo(new BigDecimal(0)) <= 0){
                    data.setErrorMsg("房源价格必须大于0");
                    failList.add(data);
                    return false;
                }
                tradeDetailDTO.setRoomPrice(roomPrice);
            } catch (Exception e) {
                data.setErrorMsg("房源价格格式错误");
                failList.add(data);
                return false;
            }
        }
        if(StringUtils.isEmpty(data.getRoomArea())){
            data.setErrorMsg("房源面积不能为空");
            failList.add(data);
            return false;
        }else{
            try {
                BigDecimal roomArea = new BigDecimal(String.valueOf(data.getRoomArea()));
                if(roomArea.compareTo(new BigDecimal(0)) <= 0){
                    data.setErrorMsg("房源面积必须大于0");
                    failList.add(data);
                    return false;
                }
                tradeDetailDTO.setRoomArea(roomArea);
            } catch (Exception e) {
                data.setErrorMsg("房源面积格式错误");
                failList.add(data);
                return false;
            }
        }
        if(StringUtils.isEmpty(data.getTradeDate())){
            data.setErrorMsg("成交时间不能为空");
            failList.add(data);
            return false;
        }
        return true;
    }

    /**
     * 所有数据读取完之后调用该方法
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if(!CollectionUtils.isEmpty(successList)){
            if(returnVo.getSuccessNum() == null){
                returnVo.setSuccessNum(successList.size());
            }else {
                returnVo.setSuccessNum(returnVo.getSuccessNum() + successList.size());
            }
            log.info("存储成交明细数据, size:{}, successList:{}", successList.size(), JSON.toJSONString(successList));
            dictImportTradeDetailRemote.batchInsert(successList);
            log.info("存储成交明细数据SUCCESS, size:{}", successList.size());
        }
        returnVo.setFailList(failList);
        returnVo.setFailNum(failList.size());
        returnVo.setImportTotalNum(returnVo.getSuccessNum() + returnVo.getFailNum());
        log.info("存储成交明细数据导入成功, size:{}, returnVo:{}", successList.size(), JSON.toJSONString(returnVo));
    }

    /**
     * 分批存储数据库
     */
    private void saveData() {
        if(returnVo.getSuccessNum() == null){
            returnVo.setSuccessNum(successList.size());
        }else {
            returnVo.setSuccessNum(returnVo.getSuccessNum() + successList.size());
        }
        log.info("存储成交明细数据, size:{}, successList:{}", successList.size(), JSON.toJSONString(successList));
        dictImportTradeDetailRemote.batchInsert(successList);
        log.info("存储成交明细数据SUCCESS, size:{}", successList.size());
    }

    /**
     * 获取导入结果
     * @return
     */
    @Override
    public ImportDataReturnVo<ImportTradeDetailExcelMode> getResult() {
        return returnVo;
    }
}

3.2、编写导入工具类

/**
 * @author: huangyibo
 * @Date: 2022/3/24 20:11
 * @Description:
 */

public class EasyExcelUtil {

    /**
     * 下载文件时,针对不同浏览器,进行附件名的编码
     *
     * @param filename 下载文件名
     *
     * @param agent 客户端浏览器
     *
     * @return 编码后的下载附件名
     * @throws IOException
     */
    public static String encodeDownloadFilename(String filename, String agent)
            throws IOException {
        if (agent.contains("Firefox")) { // 火狐浏览器
            filename = "=?UTF-8?B?"
                    + Base64.getEncoder().encode(filename.getBytes("utf-8"))
                    + "?=";
            filename = filename.replaceAll("\r\n", "");
        } else { // IE及其他浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+"," ");
        }
        return filename;
    }


    /**
     * 设置导出Excel格式
     * @param response
     * @param request
     * @param filename
     * @throws IOException
     */
    public static void setExportExcelFormat(HttpServletResponse response, HttpServletRequest request, String filename) throws IOException {
        String agent = request.getHeader("user-agent");//获得游览器
        filename = filename + ".xlsx";
        String downloadFilename = encodeDownloadFilename(filename, agent); //使用工具类解决文件乱码的问题
        response.setCharacterEncoding("UTF-8");
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        // 下载文件的默认名称
        response.setHeader("Content-Disposition", "attachment;filename="+downloadFilename);
        response.setHeader("Pragma", "public");
        response.setHeader("Cache-Control", "no-store");
        response.addHeader("Cache-Control", "max-age=0");
    }


    /**
     * 读取导入excel数据(单sheet页读取)
     * @param file              上传文件
     * @param clazz             导入实体类型
     * @param excelListener     数据导入监听器
     * @return ImportDataReturnVo<?>
     * @throws IOException
     */
    public static ImportDataReturnVo<?> read(MultipartFile file, Class<?> clazz, ImportDataAbstractListener<?> excelListener) throws IOException {
        EasyExcel.read(file.getInputStream(), clazz, excelListener).sheet().doRead();
        return excelListener.getResult();
    }


    /**
     * 读取导入excel数据(读取全部sheet页)
     * @param file              上传文件
     * @param clazz             导入实体类型
     * @param excelListener     数据导入监听器
     * @return ImportDataReturnVo<?>
     * @throws IOException
     */
    public static ImportDataReturnVo<?> readAll(MultipartFile file, Class<?> clazz, ImportDataAbstractListener<?> excelListener) throws IOException {
        EasyExcel.read(file.getInputStream(), clazz, excelListener).doReadAll();
        return excelListener.getResult();
    }


    /**
     * 读取导入excel数据(读取指定sheet页)
     * @param file              上传文件
     * @param clazz             导入实体类型
     * @param excelListener     数据导入监听器
     * @param sheetNumList      指定的sheetNum
     * @return ImportDataReturnVo<?>
     * @throws IOException
     */
    public static ImportDataReturnVo<?> readSheet(MultipartFile file, Class<?> clazz, ImportDataAbstractListener<?> excelListener, List<Integer> sheetNumList) throws IOException {
        ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build();
        // 读取不同的数据, 需要使用不同的head 和Listener
        List<ReadSheet> readSheetList = new ArrayList<>();
        sheetNumList.forEach(sheetNum -> {
            // readSheet参数设置读取sheet的序号
            ReadSheet readSheet = EasyExcel.readSheet(sheetNum).head(clazz).registerReadListener(excelListener).build();
            readSheetList.add(readSheet);
        });
        // 这里注意 一定要把readSheetList一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
        excelReader.read(readSheetList);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();
        return excelListener.getResult();
    }

    /**
     * 将数据写入Excel并写入流中
     * @param outputStream  输出流
     * @param clazz         导出实体类型
     * @param list          导出数据集合
     * @param sheetName     sheet页名称
     */
    public static void writeExcel2Stream(ByteArrayOutputStream outputStream, Class<?> clazz, List<?> list, String sheetName){
        EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(list);
    }

    /**
     * 单sheet页写入Excel
     * @param outputStream  输出流
     * @param clazz         导出实体类型
     * @param list          导出数据集合
     * @param sheetName     sheet页名称
     */
    public static void writeExcel(ServletOutputStream outputStream, Class<?> clazz, List<?> list, String sheetName) {
        EasyExcel.write(outputStream, clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet(sheetName)
                .doWrite(list);
    }

    /**
     * 多sheet页写入Excel
     * @param excelWriter   excelWriter写出对象
     * @param clazz         导出实体类型
     * @param list          导出数据集合
     * @param num           sheet页码
     * @param sheetName     sheet页名称
     */
    public static void writerSheetExcel(ExcelWriter excelWriter, Class<?> clazz, List<?> list, Integer num, String sheetName) {
        WriteSheet writeSheet = EasyExcel.writerSheet(num, sheetName).head(clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
        excelWriter.write(list, writeSheet);
    }
}

3.3、业务层调用

/**
* 导入数据源记录 前端控制器
*
* @author huangyibo
* @since 2022-03-11
*/

@Lazy
@RestController
@RequestMapping("/security/dictImportDataRecord")
@Api(tags = ApiSwaggerTags.DICT_IMPORT,value = "导入数据源记录")
@Slf4j
public class DictImportDataRecordController extends WebBaseController {

    @Resource
    private DictImportDataRecordRemote dictImportDataRecordRemote;

    @Resource
    private DictImportTradeDetailRemote dictImportTradeDetailRemote;

    @Resource
    private AliyunOss aliyunOss;

    @PostMapping("/importTrade")
    @ApiOperation(value = "导入成交明细数据")
    @ApiResponses({
            @ApiResponse(message = "导入成交明细数据", code = 200, response = ImportDataReturnVo.class)
    })
    public String importTradeData(@RequestParam("file") MultipartFile file) {
        JwtUserInfo userInfo = getCurrentUserInfo();
        DictImportDataRecordDTO dataRecordDTO = new DictImportDataRecordDTO();
        ImportDataReturnVo<?> result = new ImportDataReturnVo<>();
        try {
            TradeDetailImportListener excelListener = new TradeDetailImportListener(dictImportTradeDetailRemote, userInfo.getUserId());
            result = EasyExcelUtil.read(file, ImportTradeDetailExcelMode.class, excelListener);
            if(!CollectionUtils.isEmpty(result.getFailList())){
                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                //将格式错误数据写入Excel并写入流中
                EasyExcelUtil.writeExcel2Stream(outputStream, ImportTradeDetailExcelMode.class, result.getFailList(), "成交数据");
                MultipartFile multipartFile = FileUtil.getMultipartFileFromStream(outputStream, "成交数据错误数据.xlsx");
                //上传aliyun 对象存储 OSS
                AliyunUploadRequest uploadRequest = new AliyunUploadRequest(multipartFile);
                AliyunUploadResult uploadResult = aliyunOss.fileUpload(uploadRequest);
                log.info("导入成交明细数据, 校验不通过数据写入excel上传到阿里云oss成功, result:{}", JSON.toJSONString(result));
                if(uploadResult != null){
                    dataRecordDTO.setFailDataFileUrl(uploadResult.getUrl());
                }
            }
            BeanUtils.copyProperties(result, dataRecordDTO);
            dataRecordDTO.setDataType(DataTypeEnum.TRADE_DATA);
            dataRecordDTO.setCreateId(userInfo.getUserId());
            dataRecordDTO.setModifyId(userInfo.getUserId());
            dataRecordDTO.setId(result.getImportId());
            dictImportDataRecordRemote.insert(dataRecordDTO);
            result.setSuccess(Boolean.TRUE);
        } catch (Exception e) {
            result.setSuccess(Boolean.FALSE);
            result.setErrCode(DictReturnCodeEnum.F6000000.name());
            if(e instanceof RequestResultException){
                result.setErrMessage(e.getMessage());
            }else {
                result.setErrMessage(DictReturnCodeEnum.F6000000.getMessage());
            }
            log.info("导入成交明细数据 Exception, result:{}", JSON.toJSONString(result), e);
        }
        return successInfo(result);
    }
}

四、导出

  • EASYEXCEL导出百万级连表查询XLSX数据方法实测102万105秒

4.1 编写excel导出对应实体类

@Data
@ApiModel(description="估价表")
public class DictGardenAppraisalExport implements Serializable {

    @ApiModelProperty(value = "城市名称")
    @ColumnWidth(20)
    @ExcelProperty(value = "城市", index = 0)
    private String cityName;

    @ApiModelProperty(value = "城市区域名称")
    @ColumnWidth(20)
    @ExcelProperty(value = "区域", index = 1)
    private String regionName;

    @ApiModelProperty(value = "商圈名称")
    @ColumnWidth(20)
    @ExcelProperty(value = "商圈", index = 2)
    private String businessAreaName;

    @ApiModelProperty(value = "楼盘名称")
    @ColumnWidth(20)
    @ExcelProperty(value = "楼盘名称", index = 3)
    private String gardenName;

    @ApiModelProperty(value = "物业类型(冗余字段,存储格式:普通住宅/公寓/酒店)")
    @ColumnWidth(20)
    @ExcelProperty(value = "物业类型", index = 4)
    private String usageName;

    @ApiModelProperty(value = "委托套数")
    @ColumnWidth(20)
    @ExcelProperty(value = "委托套数", index = 5)
    private Integer trustNum;

    @ApiModelProperty(value = "委托均价")
    @ColumnWidth(20)
    @ExcelProperty(value = "委托均价", index = 6)
    private BigDecimal trustAvgPrice;

    @ApiModelProperty(value = "成交套数")
    @ColumnWidth(20)
    @ExcelProperty(value = "成交套数", index = 7)
    private Integer tradeNum;

    @ApiModelProperty(value = "成交均价")
    @ColumnWidth(20)
    @ExcelProperty(value = "成交均价", index = 8)
    private BigDecimal tradeAvgPrice;

    @ApiModelProperty(value = "外部参考价")
    @ColumnWidth(20)
    @ExcelProperty(value = "外部参考价", index = 9)
    private BigDecimal outerReferPrice;

    @ApiModelProperty(value = "参考价")
    @ColumnWidth(20)
    @ExcelProperty(value = "参考价", index = 10)
    private BigDecimal referPrice;

    @ApiModelProperty(value = "政府指导价")
    @ColumnWidth(20)
    @ExcelProperty(value = "政府指导价", index = 11)
    private BigDecimal governGuidePrice;

    @ApiModelProperty(value = "价差幅度")
    @ColumnWidth(20)
    @ExcelProperty(value = "价差幅度", index = 12)
    @NumberFormat("##0.##0%")
    private BigDecimal diffPriceRange;

    @ApiModelProperty(value = "参考价/第三方委托均价")
    @ColumnWidth(20)
    @ExcelProperty(value = "幅度1", index = 13)
    @NumberFormat("##0.##0%")
    private BigDecimal referThirdTrust;

    @ApiModelProperty(value = "参考价/第三方成交均价")
    @ColumnWidth(20)
    @ExcelProperty(value = "幅度2", index = 14)
    @NumberFormat("##0.##0%")
    private BigDecimal referThirdTrade;

    @ApiModelProperty(value = "参考价/第三方参考价")
    @ColumnWidth(20)
    @ExcelProperty(value = "幅度3", index = 15)
    @NumberFormat("##0.##0%")
    private BigDecimal referThirdRefer;

    @ApiModelProperty(value = "估价日期")
    @ColumnWidth(20)
    @ExcelProperty(value = "估价日期", index = 16)
    @DateTimeFormat("yyyy-MM-dd")
    private Date referDate;

    @ApiModelProperty(value = "发布状态:已发布=PUBLISHED、待发布=TOBE_PUBLISH、已撤回=REVOKE")
    @ColumnWidth(20)
    @ExcelProperty(value = "状态", index = 17)
    private String publishStatus;
}

4.2导入逻辑编写

@PostMapping(value = "/exportAppraisal")
@ApiOperation(value = "导出估价信息")
@ApiResponses({
        @ApiResponse(message = "导出估价信息", code = 200, response = DictGardenAppraisalVo.class)
})
public String exportAppraisal(@RequestBody DictGardenAppraisalQueryPageForm queryPageForm, HttpServletResponse response, HttpServletRequest request) {
    try {
        ServletOutputStream outputStream = response.getOutputStream();
        queryPageForm.setPageSize(PAGE_SIZE);
        Pagination<DictGardenAppraisalVo> page = dictGardenAppraisalRemote.queryDictGardenAppraisalPage(queryPageForm);
        EasyExcelUtil.setExportExcelFormat(response,request,"估价信息");
        //如果总数据量多余10万,分页导出
        if(page.getRecordCount() > PAGE_SIZE){
            //必须放到循环外,否则会刷新流
            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
            for (int i = 1; i <= page.getPageCount(); i++) {
                queryPageForm.setCurrentPage(i);
                Pagination<DictGardenAppraisalVo> appraisalPage = dictGardenAppraisalRemote.queryDictGardenAppraisalPage(queryPageForm);
                if (!CollectionUtils.isEmpty(appraisalPage.getItems())) {
                    List<DictGardenAppraisalExport> exportList = getDictGardenAppraisalExports(appraisalPage);
                    WriteSheet writeSheet = EasyExcel.writerSheet(i, "估价信息" + i).head(DictGardenAppraisalExport.class)
                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
                    excelWriter.write(exportList, writeSheet);
                }
            }
            //刷新流
            excelWriter.finish();
        }else {
            if (!CollectionUtils.isEmpty(page.getItems())) {
                List<DictGardenAppraisalExport> exportList = getDictGardenAppraisalExports(page);
                EasyExcel.write(outputStream,DictGardenAppraisalExport.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("估价信息").doWrite(exportList);
            }
        }
        outputStream.flush();
        response.getOutputStream().close();
        return successInfo(true);
    } catch (Exception e) {
        log.error("估价信息数据导出excel Exception",e);
        return successInfo(false);
    }
}
//@AuthorityAnnotation(AuthorityAnnotationEnums.ADMIN)
@PostMapping(value = "/exportAppraisal")
@ApiOperation(value = "导出楼盘估价信息")
@ApiResponses({
        @ApiResponse(message = "导出楼盘估价信息", code = 200, response = Boolean.class)
})
public String exportAppraisal(@RequestBody DictGardenAppraisalQueryPageForm queryPageForm, HttpServletResponse response, HttpServletRequest request) {
    try {
        ServletOutputStream outputStream = response.getOutputStream();
        queryPageForm.setPageSize(PAGE_SIZE);
        Pagination<DictGardenAppraisalVo> page = dictGardenAppraisalRemote.queryDictGardenAppraisalPage(queryPageForm);
        EasyExcelUtil.setExportExcelFormat(response, request,"KF楼盘估价");
        //如果总数据量多余10万,分页导出
        if(page.getRecordCount() > PAGE_SIZE){
            //必须放到循环外,否则会刷新流
            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
            for (int i = 1; i <= page.getPageCount(); i++) {
                queryPageForm.setCurrentPage(i);
                Pagination<DictGardenAppraisalVo> appraisalPage = dictGardenAppraisalRemote.queryDictGardenAppraisalPage(queryPageForm);
                if (!CollectionUtils.isEmpty(appraisalPage.getItems())) {
                    List<DictGardenAppraisalExport> exportList = getDictGardenAppraisalExports(appraisalPage);
                    EasyExcelUtil.writerSheetExcel(excelWriter, DictGardenAppraisalExport.class, exportList, i, "楼盘估价" + i);
                }
            }
            //刷新流
            excelWriter.finish();
        }else {
            //总数据量小余10万,单页导出
            if (!CollectionUtils.isEmpty(page.getItems())) {
                List<DictGardenAppraisalExport> exportList = getDictGardenAppraisalExports(page);
                EasyExcelUtil.writeExcel(outputStream, DictGardenAppraisalExport.class, exportList, "楼盘估价");
            }
        }
        outputStream.flush();
        outputStream.close();
        return successInfo(true);
    } catch (Exception e) {
        log.error("楼盘估价数据导出excel Exception",e);
        return successInfo(false);
    }
}

实测:导出用时105秒左右

每个表10万条

参考:
https://www.yuque.com/easyexcel/doc/easyexcel

https://alibaba-easyexcel.github.io/

https://www.freesion.com/article/65771115640/

https://www.cnblogs.com/math-and-it/p/15465848.html

https://blog.51cto.com/alex4dream/4112300

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350