Java真的不难(五十一)SpringBoot使用EasyExcel实现导出

EasyExcel:

大家好久不见!

一、什么是EasyExcel?

EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
github地址: https://github.com/alibaba/easyexcel
官方文档地址https://www.yuque.com/easyexcel/doc/easyexcel

当然还有一个POI也可以实现操作Excel,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能,POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”

EasyExcel和POI的区别:
在这里插入图片描述


二、EasyExcel的实际使用

首先导入依赖:

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

依赖版本按需使用即可,一些基本的操作大家看官方文档即可,在这里就记录一下自己通过EasyExcel导出的表格及过程

首先看一下需要导出的模板:

在这里插入图片描述
区域说明:
A:单个单元格填充
B:列表填充
C:列表填充(但单元格格式与B区域不同)
D:单个单元格填充

一、A区域的填充数据模板的设置:

这个区域填充还是很简单的,只要给每个表格取个参数名,用{}包裹,Java里面用Map来填充即可。Map的键对应{}里面的参数名,对应的值就是填充进去的值:
在这里插入图片描述
例如这样,以下数据都是从数据库查出后填入Map即可:

 HashMap<String, Object> workOrderFileData = new HashMap<>();
        for (ProjectWorkOrderFileData p : WorkOrderFileList) {
    
    
            workOrderFileData.put("projectCode", p.getProjectCode());
            workOrderFileData.put("projectName", p.getProjectName());
            workOrderFileData.put("inspectedEnt", p.getInspectedEnt());
            workOrderFileData.put("customerAddress", p.getCustomerAddress());
            workOrderFileData.put("linkMan", p.getLinkMan());
            workOrderFileData.put("linkPhone", p.getLinkPhone());
            workOrderFileData.put("bizCreateTime", p.getBizCreateTime().substring(0, 10));
            workOrderFileData.put("customerName", p.getCustomerName());
            workOrderFileData.put("prodCompany", p.getProdCompany());
            workOrderFileData.put("testCode", p.getTestCode());
            workOrderFileData.put("batchCode", p.getBatchCode());
            workOrderFileData.put("region", region);
 }

二、B和C区域的填充数据模板的设置:

扫描二维码关注公众号,回复: 15328039 查看本文章

因为B区域是有两个列表,所以我们一定在模板内区分开来,给每个列表取个参数名,然后给每个字段取个参数名,通过列表参数名点(.)字段参数名即可:
即:{} 代表普通变量, {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list
注意: 用{} 来表示你要用的变量 如果本来就有"{“,”}" 特殊字符 用"{“,”}"代替
在这里插入图片描述
对于这两组数据,Java里面使用两个列表储存即可,列表内可以存一个对象:

ArrayList<SampleData> SampleDataList = service.selectSampleData(proId);
ArrayList<AnalysisMethodData> analysisMethodDataList = service.selectAnalysisMethodData(proId);

对象里面的字段名即是该列表所有字段名:

@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class SampleData {
    
    
    private int number;
    private String typeName;
    private String redFolderName;
    private String redAnalyzeItems;
    private int spotFrequency;
    private int frequency;
    private int day;
    private int cycleOrder;
    private int timesOrder;
    private int sampleOrder;
    private String remarks;
}

D区域的填充和A区域是一样的,通过Map来填充即可,因为是一对一的关系


三、两个列表之间单元格样式不一样的解决办法

通过模板可以看到BC两个列表之前的单元格样式不一样,如果不做处理,在填充的时候,C区域的样式会根据B区域的样式来填充,也就是C区域合并的单元格会被拆分,所以需要一个工具类:

public class MyHandler extends AbstractMergeStrategy {
    
    

    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
    
    
        if (relativeRowIndex == null || relativeRowIndex == 0) {
    
    
            return;
        }
        int rowIndex = cell.getRowIndex();
        int colIndex = cell.getColumnIndex();
        sheet = cell.getSheet();
        Row preRow = sheet.getRow(rowIndex - 1);
        Cell preCell = preRow.getCell(colIndex);//获取上一行的该格
        List<CellRangeAddress> list = sheet.getMergedRegions();
        CellStyle cs = cell.getCellStyle();
        cell.setCellStyle(cs);
        for (CellRangeAddress cellRangeAddress : list) {
    
    
            if (cellRangeAddress.containsRow(preCell.getRowIndex()) && cellRangeAddress.containsColumn(preCell.getColumnIndex())) {
    
    
                int lastColIndex = cellRangeAddress.getLastColumn();
                int firstColIndex = cellRangeAddress.getFirstColumn();
                CellRangeAddress cra = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), firstColIndex, lastColIndex);
                sheet.addMergedRegion(cra);
                RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet);
                RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet);
                RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet);
                RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet);
                return;
            }
        }
    }
}

直接复制当工具类就完事!


三、完成其他配置,开始填充

我们还要设置两个地址:模板的位置以及导出后文件的储存位置,以下是控制层完整代码

@GetMapping(path = "/generateProjectWorkOrder")
    public JsonResp<String> generateProjectWorkOrder(@RequestParam("proId") String proId) {
    
    
        //设置模板位置以及导出后储存位置
        String templateFile = "D:/WorkProject/ExcelTemplate/模板.xls";
        String resultFile = "D:/WorkProject/ExcelTemplate/WorkOrderFile/" +"导出结果.xls";
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
        ExcelWriter excelWriter = EasyExcel.write(resultFile).withTemplate(templateFile).build();
        //使用让该组单元格格式不受影响的工具类
        WriteSheet sheet = EasyExcel.writerSheet(0).registerWriteHandler(new MyHandler()).build();
        //获取数据(分别是ABCD四组数据)
        HashMap<String, Object> workOrderFileData = service.selectWorkOrder(proId);
        ArrayList<SampleData> SampleDataList = service.selectSampleData(proId);
        ArrayList<AnalysisMethodData> analysisMethodDataList = service.selectAnalysisMethodData(proId);
        HashMap<String, String> reviewerData = service.selectReviewerData(proId);
        //单组填充(A区域)
        excelWriter.fill(workOrderFileData, sheet);
        //多个列表填充(B、C区域)
        // 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹
        excelWriter.fill(new FillWrapper("data1", SampleDataList), fillConfig, sheet);
        excelWriter.fill(new FillWrapper("data2", analysisMethodDataList), fillConfig, sheet);
        //单组填充(D区域)
        excelWriter.fill(reviewerData, sheet);
        //关闭流
        excelWriter.finish();
        //返回数据
        JsonResp<String> objectJsonResp = new JsonResp<>();
        //返回路径
        resultFile = "lims/downloadGenerateExcel?path=" + resultFile;
        objectJsonResp.setData(resultFile);
        return objectJsonResp;
    }

数据获取的方式按照自己的格式来即可,若完成以上编写,可以实现本地的导出与下载,若需要部署在服务器上,则还需要从服务器下载到本地


三、服务器项目导出后下载到本地

若把项目部署在服务器上,使用该功能首先是下载到服务器上,然后再从服务器上下载到本地电脑。
我们可以使用HttpServletResponse response 来操作:
代码如下:

    @GetMapping(path = "/downloadGenerateExcel")
    public void download(String path, HttpServletResponse response) {
    
    
        try {
    
    
            // path是指想要下载的文件的路径
            File file = new File(path);
            String filename = file.getName();
            // 将文件写入输入流
            FileInputStream fileInputStream = new FileInputStream(file);
            InputStream fis = new BufferedInputStream(fileInputStream);
            byte[] buffer = new byte[fis.available()];
            //noinspection ResultOfMethodCallIgnored
            fis.read(buffer);
            fis.close();
            response.reset();
            response.setCharacterEncoding("UTF-8");
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
            // 告知浏览器文件的大小
            response.addHeader("Content-Length", "" + file.length());
            OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
            response.setContentType("application/octet-stream");
            outputStream.write(buffer);
            outputStream.flush();
        } catch (IOException ex) {
    
    
            ex.printStackTrace();
        }
    }

这个方法的参数就是上述方法最后的代码:

//返回数据
JsonResp<String> objectJsonResp = new JsonResp<>();
//返回路径
resultFile = "lims/downloadGenerateExcel?path=" + resultFile;
objectJsonResp.setData(resultFile);
return objectJsonResp;

也就是导出完成后返回的参数可以自动调用这个方法,把返回的参数自动传入该方法的Path,即可完成从服务器下载到本地。


四、使用效果

运行对应的接口,即可把数据导出并填充在指定的Excel模板内,并下载到指定位置,使用以上模板导出的结果如下:
在这里插入图片描述
以上均为模拟数据,大家根据实际情况来编写即可

在最后附上EasyExcel最新的文档地址https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill


在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/m0_57310550/article/details/127805964